Expose signing operations in DiceOps

The CBOR certificate generator previously had a dependency on boringssl
that meant it couldn't be reused with a different crypto implementation.
Remove the dependency by adding the needed operations to DiceOps so the
client can provide a suitable implementation. There are boringssl
implementations provided that can be used where boringssl is available.

The other certificate generators are tied to a crypto implementation so
don't need these operations to be provided.

Change-Id: Iee27061444da48c6dd2ef4cf91676b45fb227c7c
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/40021
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/BUILD.gn b/BUILD.gn
index 1049920..a6290c5 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -49,7 +49,7 @@
   public = [ "include/dice/dice.h" ]
   sources = [
     "src/boringssl_cert_op.c",
-    "src/boringssl_hash_kdf_ops.c",
+    "src/boringssl_hash_kdf_sign_ops.c",
     "src/dice.c",
   ]
   deps = [
@@ -73,7 +73,7 @@
 pw_static_library("dice_with_cbor_cert") {
   public = [ "include/dice/dice.h" ]
   sources = [
-    "src/boringssl_hash_kdf_ops.c",
+    "src/boringssl_hash_kdf_sign_ops.c",
     "src/cbor_cert_op.c",
     "src/dice.c",
   ]
@@ -87,7 +87,7 @@
 pw_static_library("dice_with_cbor_template_cert") {
   public = [ "include/dice/dice.h" ]
   sources = [
-    "src/boringssl_hash_kdf_ops.c",
+    "src/boringssl_hash_kdf_sign_ops.c",
     "src/dice.c",
     "src/template_cbor_cert_op.c",
   ]
@@ -100,7 +100,7 @@
 pw_static_library("dice_with_x509_template_cert") {
   public = [ "include/dice/dice.h" ]
   sources = [
-    "src/boringssl_hash_kdf_ops.c",
+    "src/boringssl_hash_kdf_sign_ops.c",
     "src/dice.c",
     "src/template_cert_op.c",
   ]
diff --git a/include/dice/boringssl_ops.h b/include/dice/boringssl_ops.h
index 51d58cd..410c41d 100644
--- a/include/dice/boringssl_ops.h
+++ b/include/dice/boringssl_ops.h
@@ -33,6 +33,22 @@
                          const uint8_t* info, size_t info_size,
                          uint8_t* output);
 
+DiceResult DiceBsslEd25519KeypairFromSeed(
+    const DiceOps* ops_not_used, const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    uint8_t public_key[DICE_PUBLIC_KEY_MAX_SIZE], size_t* public_key_size,
+    uint8_t private_key[DICE_PRIVATE_KEY_MAX_SIZE], size_t* private_key_size);
+
+DiceResult DiceBsslEd25519Sign(const DiceOps* ops, const uint8_t* message,
+                               size_t message_size, const uint8_t* private_key,
+                               size_t private_key_size, size_t signature_size,
+                               uint8_t* signature);
+
+DiceResult DiceBsslEd25519Verify(const DiceOps* ops, const uint8_t* message,
+                                 size_t message_size, const uint8_t* signature,
+                                 size_t signature_size,
+                                 const uint8_t* public_key,
+                                 size_t public_key_size);
+
 DiceResult DiceBsslGenerateCertificateOp(
     const DiceOps* ops,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
diff --git a/include/dice/cbor_cert_op.h b/include/dice/cbor_cert_op.h
index bc5b28c..5bf5571 100644
--- a/include/dice/cbor_cert_op.h
+++ b/include/dice/cbor_cert_op.h
@@ -24,6 +24,10 @@
 // This function implements the 'DiceOps::generate_certificate' callback
 // documented in dice.h. It generates a CWT-style CBOR certificate using the
 // ED25519-SHA512 signature scheme.
+//
+// The |keypair_from_seed| and |sign| operations in |ops| are required by this
+// function. |verify| is optional but will be used as a safety check of the
+// signature if it is provided.
 DiceResult DiceGenerateCborCertificateOp(
     const DiceOps* ops,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
diff --git a/include/dice/dice.h b/include/dice/dice.h
index ede56d2..647604f 100644
--- a/include/dice/dice.h
+++ b/include/dice/dice.h
@@ -27,6 +27,8 @@
 #define DICE_HIDDEN_SIZE 64
 #define DICE_INLINE_CONFIG_SIZE 64
 #define DICE_PRIVATE_KEY_SEED_SIZE 32
+#define DICE_PUBLIC_KEY_MAX_SIZE 48
+#define DICE_PRIVATE_KEY_MAX_SIZE 64
 
 typedef enum {
   kDiceResultOk,
@@ -114,6 +116,40 @@
                     size_t ikm_size, const uint8_t* salt, size_t salt_size,
                     const uint8_t* info, size_t info_size, uint8_t* output);
 
+  // Deterministically generates a public and private key pair from |seed|.
+  // Since this is deterministic, |seed| is as sensitive as a private key and
+  // can be used directly as the private key. The |private_key| may use an
+  // implementation defined format so may only be passed to the |sign|
+  // operation.
+  //
+  // This operation is optional unless otherwise documented.
+  DiceResult (*keypair_from_seed)(
+      const DiceOps* ops, const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE],
+      uint8_t public_key[DICE_PUBLIC_KEY_MAX_SIZE], size_t* public_key_size,
+      uint8_t private_key[DICE_PRIVATE_KEY_MAX_SIZE], size_t* private_key_size);
+
+  // Calculates a signature of |message_size| bytes from |message| using
+  // |private_key|. |private_key| was generated by |keypair_from_seed| to allow
+  // an implementation to use their own private key format. |signature| points
+  // to |signature_size| bytes into which the calculated signature is written.
+  // If |signature_size| differs from the implementation's signature size,
+  // kDiceResultPlatformError is returned.
+  //
+  // This operation is optional unless otherwise documented.
+  DiceResult (*sign)(const DiceOps* ops, const uint8_t* message,
+                     size_t message_size, const uint8_t* private_key,
+                     size_t private_key_size, size_t signature_size,
+                     uint8_t* signature);
+
+  // Verifies, using |public_key|, that |signature| covers |message_size| bytes
+  // from |message|.
+  //
+  // This operation is optional unless otherwise documented.
+  DiceResult (*verify)(const DiceOps* ops, const uint8_t* message,
+                       size_t message_size, const uint8_t* signature,
+                       size_t signature_size, const uint8_t* public_key,
+                       size_t public_key_size);
+
   // Generates an X.509 certificate, or an alternative certificate format, from
   // the given |subject_private_key_seed| and |input_values|, and signed by
   // |authority_private_key_seed|. The subject private key seed is supplied
diff --git a/include/dice/template_cbor_cert_op.h b/include/dice/template_cbor_cert_op.h
index c756701..284a054 100644
--- a/include/dice/template_cbor_cert_op.h
+++ b/include/dice/template_cbor_cert_op.h
@@ -26,12 +26,14 @@
 // template using the ED25519-SHA512 signature scheme.
 //
 // If no variable length descriptors are used in a DICE certificate, the
-// certificate can be constructed from a template instead of using a CBOR / COSE
-// library. This implementation includes only hashes and inline configuration in
-// the certificate fields. For convenience this uses the lower level curve25519
-// implementation in boringssl but does not use any CBOR or COSE library. This
-// approach may be especially useful in very low level components where
-// simplicity is paramount.
+// certificate can be constructed from a template instead of using a CBOR /
+// COSE library. This implementation includes only hashes and inline
+// configuration in the certificate fields. This approach may be especially
+// useful in very low level components where simplicity is paramount.
+//
+// The |keypair_from_seed| and |sign| operations in |ops| are required by this
+// function. |verify| is optional but will be used as a safety check of the
+// signature if it is provided.
 //
 // This function will return kDiceResultInvalidInput if 'input_values' specifies
 // any variable length descriptors. In particular:
diff --git a/src/boringssl_hash_kdf_ops.c b/src/boringssl_hash_kdf_ops.c
deleted file mode 100644
index fb4692b..0000000
--- a/src/boringssl_hash_kdf_ops.c
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 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.
-
-#include <stdint.h>
-
-#include "dice/boringssl_ops.h"
-#include "dice/dice.h"
-#include "openssl/evp.h"
-#include "openssl/hkdf.h"
-#include "openssl/is_boringssl.h"
-#include "openssl/sha.h"
-
-DiceResult DiceBsslHashOp(const DiceOps* ops_not_used, const uint8_t* input,
-                          size_t input_size, uint8_t output[DICE_HASH_SIZE]) {
-  (void)ops_not_used;
-  SHA512(input, input_size, output);
-  return kDiceResultOk;
-}
-
-DiceResult DiceBsslKdfOp(const DiceOps* ops_not_used, size_t length,
-                         const uint8_t* ikm, size_t ikm_size,
-                         const uint8_t* salt, size_t salt_size,
-                         const uint8_t* info, size_t info_size,
-                         uint8_t* output) {
-  (void)ops_not_used;
-  if (!HKDF(output, length, EVP_sha512(), ikm, ikm_size, salt, salt_size, info,
-            info_size)) {
-    return kDiceResultPlatformError;
-  }
-  return kDiceResultOk;
-}
diff --git a/src/boringssl_hash_kdf_sign_ops.c b/src/boringssl_hash_kdf_sign_ops.c
new file mode 100644
index 0000000..a1cf5a9
--- /dev/null
+++ b/src/boringssl_hash_kdf_sign_ops.c
@@ -0,0 +1,94 @@
+// Copyright 2020 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.
+
+#include <stdint.h>
+
+#include "dice/boringssl_ops.h"
+#include "dice/dice.h"
+#include "openssl/curve25519.h"
+#include "openssl/evp.h"
+#include "openssl/hkdf.h"
+#include "openssl/is_boringssl.h"
+#include "openssl/sha.h"
+
+DiceResult DiceBsslHashOp(const DiceOps* ops_not_used, const uint8_t* input,
+                          size_t input_size, uint8_t output[DICE_HASH_SIZE]) {
+  (void)ops_not_used;
+  SHA512(input, input_size, output);
+  return kDiceResultOk;
+}
+
+DiceResult DiceBsslKdfOp(const DiceOps* ops_not_used, size_t length,
+                         const uint8_t* ikm, size_t ikm_size,
+                         const uint8_t* salt, size_t salt_size,
+                         const uint8_t* info, size_t info_size,
+                         uint8_t* output) {
+  (void)ops_not_used;
+  if (!HKDF(output, length, EVP_sha512(), ikm, ikm_size, salt, salt_size, info,
+            info_size)) {
+    return kDiceResultPlatformError;
+  }
+  return kDiceResultOk;
+}
+
+DiceResult DiceBsslEd25519KeypairFromSeed(
+    const DiceOps* ops_not_used, const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    uint8_t public_key[DICE_PUBLIC_KEY_MAX_SIZE], size_t* public_key_size,
+    uint8_t private_key[DICE_PRIVATE_KEY_MAX_SIZE], size_t* private_key_size) {
+  (void)ops_not_used;
+#if DICE_PRIVATE_KEY_SEED_SIZE != 32
+#error "Private key seed is expected to be 32 bytes."
+#endif
+#if DICE_PUBLIC_KEY_MAX_SIZE < 32
+#error "Ed25519 needs 32 bytes to store the public key."
+#endif
+#if DICE_PRIVATE_KEY_MAX_SIZE < 64
+#error "This Ed25519 implementation needs  64 bytes for the private key."
+#endif
+  ED25519_keypair_from_seed(public_key, private_key, seed);
+  *public_key_size = 32;
+  *private_key_size = 64;
+  return kDiceResultOk;
+}
+
+DiceResult DiceBsslEd25519Sign(const DiceOps* ops_not_used,
+                               const uint8_t* message, size_t message_size,
+                               const uint8_t* private_key,
+                               size_t private_key_size, size_t signature_size,
+                               uint8_t* signature) {
+  (void)ops_not_used;
+  if (private_key_size != 64 || signature_size != 64) {
+    return kDiceResultPlatformError;
+  }
+  if (1 != ED25519_sign(signature, message, message_size, private_key)) {
+    return kDiceResultPlatformError;
+  }
+  return kDiceResultOk;
+}
+
+DiceResult DiceBsslEd25519Verify(const DiceOps* ops_not_used,
+                                 const uint8_t* message, size_t message_size,
+                                 const uint8_t* signature,
+                                 size_t signature_size,
+                                 const uint8_t* public_key,
+                                 size_t public_key_size) {
+  (void)ops_not_used;
+  if (public_key_size != 32 || signature_size != 64) {
+    return kDiceResultPlatformError;
+  }
+  if (1 != ED25519_verify(message, message_size, signature, public_key)) {
+    return kDiceResultPlatformError;
+  }
+  return kDiceResultOk;
+}
diff --git a/src/cbor_cert_op.c b/src/cbor_cert_op.c
index ac20f68..b8ed738 100644
--- a/src/cbor_cert_op.c
+++ b/src/cbor_cert_op.c
@@ -21,9 +21,6 @@
 #include "dice/cbor_writer.h"
 #include "dice/dice.h"
 #include "dice/utils.h"
-#include "openssl/curve25519.h"
-#include "openssl/is_boringssl.h"
-#include "openssl/sha.h"
 
 // Max size of COSE_Sign1 including payload.
 static const size_t kMaxCertificateSize = 2048;
@@ -95,7 +92,8 @@
 
 // Encodes a CBOR Web Token (CWT) with an issuer, subject, and additional
 // fields.
-static DiceResult EncodeCwt(const DiceInputValues* input_values,
+static DiceResult EncodeCwt(const DiceOps* ops,
+                            const DiceInputValues* input_values,
                             const char* authority_id_hex,
                             const char* subject_id_hex,
                             const uint8_t* encoded_public_key,
@@ -158,8 +156,12 @@
   // Add the config inputs.
   if (input_values->config_type == kDiceConfigTypeDescriptor) {
     uint8_t config_descriptor_hash[DICE_HASH_SIZE];
-    SHA512(input_values->config_descriptor,
-           input_values->config_descriptor_size, config_descriptor_hash);
+    DiceResult result =
+        ops->hash(ops, input_values->config_descriptor,
+                  input_values->config_descriptor_size, config_descriptor_hash);
+    if (result != kDiceResultOk) {
+      return result;
+    }
     if (
         // Add the config descriptor.
         !CborWriteInt(kConfigDescriptorLabel, &out) ||
@@ -277,8 +279,8 @@
   }
 
   // Declare buffers which are cleared on 'goto out'.
-  uint8_t subject_bssl_private_key[64];
-  uint8_t authority_bssl_private_key[64];
+  uint8_t subject_private_key[DICE_PRIVATE_KEY_MAX_SIZE];
+  uint8_t authority_private_key[DICE_PRIVATE_KEY_MAX_SIZE];
 
   // These are 'variably modified' types so need to be declared upfront.
   uint8_t encoded_public_key[kMaxPublicKeySize];
@@ -286,9 +288,15 @@
   uint8_t protected_attributes[kMaxProtectedAttributesSize];
 
   // Derive keys and IDs from the private key seeds.
-  uint8_t subject_public_key[32];
-  ED25519_keypair_from_seed(subject_public_key, subject_bssl_private_key,
-                            subject_private_key_seed);
+  uint8_t subject_public_key[DICE_PUBLIC_KEY_MAX_SIZE];
+  size_t subject_public_key_size;
+  size_t subject_private_key_size;
+  result = ops->keypair_from_seed(
+      ops, subject_private_key_seed, subject_public_key,
+      &subject_public_key_size, subject_private_key, &subject_private_key_size);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
 
   uint8_t subject_id[20];
   result = DiceDeriveCdiCertificateId(ops, subject_public_key, 32, subject_id);
@@ -300,13 +308,20 @@
                 sizeof(subject_id_hex));
   subject_id_hex[sizeof(subject_id_hex) - 1] = '\0';
 
-  uint8_t authority_public_key[32];
-  ED25519_keypair_from_seed(authority_public_key, authority_bssl_private_key,
-                            authority_private_key_seed);
+  uint8_t authority_public_key[DICE_PUBLIC_KEY_MAX_SIZE];
+  size_t authority_public_key_size;
+  size_t authority_private_key_size;
+  result = ops->keypair_from_seed(
+      ops, authority_private_key_seed, authority_public_key,
+      &authority_public_key_size, authority_private_key,
+      &authority_private_key_size);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
 
   uint8_t authority_id[20];
-  result =
-      DiceDeriveCdiCertificateId(ops, authority_public_key, 32, authority_id);
+  result = DiceDeriveCdiCertificateId(ops, authority_public_key,
+                                      authority_public_key_size, authority_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -335,7 +350,7 @@
 
   // The CWT is the payload in both the TBS and the final COSE_Sign1 structure.
   size_t payload_size = 0;
-  result = EncodeCwt(input_values, authority_id_hex, subject_id_hex,
+  result = EncodeCwt(ops, input_values, authority_id_hex, subject_id_hex,
                      encoded_public_key, encoded_public_key_size,
                      sizeof(payload), payload, &payload_size);
   if (result != kDiceResultOk) {
@@ -353,15 +368,19 @@
 
   // Sign the TBS with the authority key.
   uint8_t signature[64];
-  if (1 != ED25519_sign(signature, certificate, *certificate_actual_size,
-                        authority_bssl_private_key)) {
-    result = kDiceResultPlatformError;
+  result = ops->sign(ops, certificate, *certificate_actual_size,
+                     authority_private_key, authority_private_key_size,
+                     sizeof(signature), signature);
+  if (result != kDiceResultOk) {
     goto out;
   }
-  if (1 != ED25519_verify(certificate, *certificate_actual_size, signature,
-                          authority_public_key)) {
-    result = kDiceResultPlatformError;
-    goto out;
+  if (ops->verify) {
+    result = ops->verify(ops, certificate, *certificate_actual_size, signature,
+                         sizeof(signature), authority_public_key,
+                         authority_public_key_size);
+    if (result != kDiceResultOk) {
+      goto out;
+    }
   }
 
   // The final certificate is an untagged COSE_Sign1 structure.
@@ -370,10 +389,8 @@
       signature, certificate_buffer_size, certificate, certificate_actual_size);
 
 out:
-  ops->clear_memory(ops, sizeof(subject_bssl_private_key),
-                    subject_bssl_private_key);
-  ops->clear_memory(ops, sizeof(authority_bssl_private_key),
-                    authority_bssl_private_key);
+  ops->clear_memory(ops, sizeof(subject_private_key), subject_private_key);
+  ops->clear_memory(ops, sizeof(authority_private_key), authority_private_key);
 
   return result;
 }
diff --git a/src/cbor_cert_op_test.cc b/src/cbor_cert_op_test.cc
index 1d40bf5..84cee6a 100644
--- a/src/cbor_cert_op_test.cc
+++ b/src/cbor_cert_op_test.cc
@@ -35,11 +35,16 @@
 using dice::test::DiceStateForTest;
 using dice::test::KeyType_Ed25519;
 
-constexpr DiceOps kOps = {.context = NULL,
-                          .hash = DiceBsslHashOp,
-                          .kdf = DiceBsslKdfOp,
-                          .generate_certificate = DiceGenerateCborCertificateOp,
-                          .clear_memory = DiceClearMemory};
+constexpr DiceOps kOps = {
+    .context = NULL,
+    .hash = DiceBsslHashOp,
+    .kdf = DiceBsslKdfOp,
+    .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
+    .sign = DiceBsslEd25519Sign,
+    .verify = DiceBsslEd25519Verify,
+    .generate_certificate = DiceGenerateCborCertificateOp,
+    .clear_memory = DiceClearMemory,
+};
 
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
diff --git a/src/dice_with_cbor_cert_main.c b/src/dice_with_cbor_cert_main.c
index 7d59308..a0cd67e 100644
--- a/src/dice_with_cbor_cert_main.c
+++ b/src/dice_with_cbor_cert_main.c
@@ -24,6 +24,9 @@
   (void)argv;
   const DiceOps ops = {.hash = DiceBsslHashOp,
                        .kdf = DiceBsslKdfOp,
+                       .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
+                       .sign = DiceBsslEd25519Sign,
+                       .verify = DiceBsslEd25519Verify,
                        .generate_certificate = DiceGenerateCborCertificateOp,
                        .clear_memory = DiceClearMemory};
   uint8_t cdi_buffer[DICE_CDI_SIZE];
diff --git a/src/dice_with_cbor_template_cert_main.c b/src/dice_with_cbor_template_cert_main.c
index b54f12d..bb131d2 100644
--- a/src/dice_with_cbor_template_cert_main.c
+++ b/src/dice_with_cbor_template_cert_main.c
@@ -25,6 +25,9 @@
   const DiceOps ops = {
       .hash = DiceBsslHashOp,
       .kdf = DiceBsslKdfOp,
+      .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
+      .sign = DiceBsslEd25519Sign,
+      .verify = DiceBsslEd25519Verify,
       .generate_certificate = DiceGenerateCborCertificateFromTemplateOp,
       .clear_memory = DiceClearMemory};
   uint8_t cdi_buffer[DICE_CDI_SIZE];
diff --git a/src/template_cbor_cert_op.c b/src/template_cbor_cert_op.c
index 7fe9547..96c300b 100644
--- a/src/template_cbor_cert_op.c
+++ b/src/template_cbor_cert_op.c
@@ -24,8 +24,6 @@
 
 #include "dice/dice.h"
 #include "dice/utils.h"
-#include "openssl/curve25519.h"
-#include "openssl/is_boringssl.h"
 
 // A well-formed certificate, but with zeros in all fields to be filled.
 static const uint8_t kTemplate[441] = {
@@ -155,19 +153,26 @@
   }
 
   // Declare buffers which are cleared on 'goto out'.
-  uint8_t subject_bssl_private_key[64];
-  uint8_t authority_bssl_private_key[64];
+  uint8_t subject_private_key[DICE_PRIVATE_KEY_MAX_SIZE];
+  uint8_t authority_private_key[DICE_PRIVATE_KEY_MAX_SIZE];
 
   // These are 'variably modified' types so need to be declared upfront.
   uint8_t tbs[kTbsSize];
 
   // Derive keys and IDs from the private key seeds.
-  uint8_t subject_public_key[32];
-  ED25519_keypair_from_seed(subject_public_key, subject_bssl_private_key,
-                            subject_private_key_seed);
+  uint8_t subject_public_key[DICE_PUBLIC_KEY_MAX_SIZE];
+  size_t subject_public_key_size;
+  size_t subject_private_key_size;
+  result = ops->keypair_from_seed(
+      ops, subject_private_key_seed, subject_public_key,
+      &subject_public_key_size, subject_private_key, &subject_private_key_size);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
 
   uint8_t subject_id[20];
-  result = DiceDeriveCdiCertificateId(ops, subject_public_key, 32, subject_id);
+  result = DiceDeriveCdiCertificateId(ops, subject_public_key,
+                                      subject_public_key_size, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -175,13 +180,20 @@
   DiceHexEncode(subject_id, sizeof(subject_id), subject_id_hex,
                 sizeof(subject_id_hex));
 
-  uint8_t authority_public_key[32];
-  ED25519_keypair_from_seed(authority_public_key, authority_bssl_private_key,
-                            authority_private_key_seed);
+  uint8_t authority_public_key[DICE_PUBLIC_KEY_MAX_SIZE];
+  size_t authority_public_key_size;
+  size_t authority_private_key_size;
+  result = ops->keypair_from_seed(
+      ops, authority_private_key_seed, authority_public_key,
+      &authority_public_key_size, authority_private_key,
+      &authority_private_key_size);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
 
   uint8_t authority_id[20];
-  result =
-      DiceDeriveCdiCertificateId(ops, authority_public_key, 32, authority_id);
+  result = DiceDeriveCdiCertificateId(ops, authority_public_key,
+                                      authority_public_key_size, authority_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -208,20 +220,22 @@
          kFieldTable[kFieldIndexPayload].length);
 
   uint8_t signature[64];
-  if (1 != ED25519_sign(signature, tbs, kTbsSize, authority_bssl_private_key)) {
-    result = kDiceResultPlatformError;
+  result = ops->sign(ops, tbs, kTbsSize, authority_private_key,
+                     authority_private_key_size, sizeof(signature), signature);
+  if (result != kDiceResultOk) {
     goto out;
   }
-  if (1 != ED25519_verify(tbs, kTbsSize, signature, authority_public_key)) {
-    result = kDiceResultPlatformError;
-    goto out;
+  if (ops->verify) {
+    result = ops->verify(ops, tbs, kTbsSize, signature, sizeof(signature),
+                         authority_public_key, authority_public_key_size);
+    if (result != kDiceResultOk) {
+      goto out;
+    }
   }
   CopyField(signature, kFieldIndexSignature, certificate);
 
 out:
-  ops->clear_memory(ops, sizeof(subject_bssl_private_key),
-                    subject_bssl_private_key);
-  ops->clear_memory(ops, sizeof(authority_bssl_private_key),
-                    authority_bssl_private_key);
+  ops->clear_memory(ops, sizeof(subject_private_key), subject_private_key);
+  ops->clear_memory(ops, sizeof(authority_private_key), authority_private_key);
   return result;
 }
diff --git a/src/template_cbor_cert_op_test.cc b/src/template_cbor_cert_op_test.cc
index 5626224..d41535c 100644
--- a/src/template_cbor_cert_op_test.cc
+++ b/src/template_cbor_cert_op_test.cc
@@ -39,6 +39,9 @@
     .context = NULL,
     .hash = DiceBsslHashOp,
     .kdf = DiceBsslKdfOp,
+    .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
+    .sign = DiceBsslEd25519Sign,
+    .verify = DiceBsslEd25519Verify,
     .generate_certificate = DiceGenerateCborCertificateFromTemplateOp,
     .clear_memory = DiceClearMemory};