Move from dynamic to static dependencies

Previously, dependencies were passed in with a struct of pointers but
they will now be resolved in the linker. This removes a soure of
potential runtime programming errors.

Where DiceOps were previously passed, now there is just a context
parameter to replace the previous context parameter of DiceOps.

We're also able to merge some testing and fuzzing boilerplate code as we
no longer need to have case specific code for preparing the dynamic
dependencies.

Change-Id: I9623222b1178ea4a8dd2112a6c84993ddc26afda
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/54260
Reviewed-by: Andrew Scull <ascull@google.com>
Reviewed-by: Darren Krahn <dkrahn@google.com>
Commit-Queue: Andrew Scull <ascull@google.com>
Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index a6290c5..42d5e46 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -21,7 +21,7 @@
   deps = [
     ":fuzzers(//toolchains:host_fuzz)",
     ":optimized_libs(//toolchains:host_optimized)",
-    ":tests_run(//toolchains:host_debug)",
+    ":tests.run(//toolchains:host_debug)",
   ]
 }
 
@@ -30,7 +30,10 @@
     "include/dice/dice.h",
     "include/dice/utils.h",
   ]
-  sources = [ "src/utils.c" ]
+  sources = [
+    "src/clear_memory.c",
+    "src/utils.c",
+  ]
 }
 
 pw_source_set("cbor_writer") {
@@ -124,12 +127,8 @@
   cflags = [ "-Wno-ignored-qualifiers" ]
 }
 
-pw_source_set("fuzz_utils") {
-  public = [
-    "include/dice/dice.h",
-    "include/dice/fuzz_utils.h",
-  ]
-  sources = [ "src/fuzz_utils.cc" ]
+pw_source_set("fuzzer") {
+  sources = [ "src/fuzzer.cc" ]
 }
 
 pw_test("cbor_writer_test") {
@@ -165,11 +164,9 @@
 }
 
 pw_executable("boringssl_ops_fuzzer") {
-  sources = [ "src/boringssl_ops_fuzzer.cc" ]
   deps = [
     ":dice_with_boringssl_ops",
-    ":fuzz_utils",
-    ":utils",
+    ":fuzzer",
   ]
 }
 
@@ -183,11 +180,9 @@
 }
 
 pw_executable("template_cert_op_fuzzer") {
-  sources = [ "src/template_cert_op_fuzzer.cc" ]
   deps = [
     ":dice_with_x509_template_cert",
-    ":fuzz_utils",
-    ":utils",
+    ":fuzzer",
   ]
 }
 
@@ -201,11 +196,9 @@
 }
 
 pw_executable("cbor_cert_op_fuzzer") {
-  sources = [ "src/cbor_cert_op_fuzzer.cc" ]
   deps = [
     ":dice_with_cbor_cert",
-    ":fuzz_utils",
-    ":utils",
+    ":fuzzer",
   ]
 }
 
@@ -219,11 +212,9 @@
 }
 
 pw_executable("template_cbor_cert_op_fuzzer") {
-  sources = [ "src/template_cbor_cert_op_fuzzer.cc" ]
   deps = [
     ":dice_with_cbor_template_cert",
-    ":fuzz_utils",
-    ":utils",
+    ":fuzzer",
   ]
 }
 
@@ -237,11 +228,9 @@
 }
 
 pw_executable("mbedtls_ops_fuzzer") {
-  sources = [ "src/mbedtls_ops_fuzzer.cc" ]
   deps = [
     ":dice_with_mbedtls_ops",
-    ":fuzz_utils",
-    ":utils",
+    ":fuzzer",
   ]
 }
 
@@ -283,43 +272,42 @@
   ]
 }
 
+pw_source_set("dice_main") {
+  sources = [ "src/dice_main.c" ]
+}
+
 pw_executable("dice_with_boringssl_ops_main") {
-  sources = [ "src/dice_with_boringssl_ops_main.c" ]
   deps = [
+    ":dice_main",
     ":dice_with_boringssl_ops",
-    ":utils",
   ]
 }
 
 pw_executable("dice_with_mbedtls_ops_main") {
-  sources = [ "src/dice_with_mbedtls_ops_main.c" ]
   deps = [
+    ":dice_main",
     ":dice_with_mbedtls_ops",
-    ":utils",
   ]
 }
 
 pw_executable("dice_with_cbor_cert_main") {
-  sources = [ "src/dice_with_cbor_cert_main.c" ]
   deps = [
+    ":dice_main",
     ":dice_with_cbor_cert",
-    ":utils",
   ]
 }
 
 pw_executable("dice_with_cbor_template_cert_main") {
-  sources = [ "src/dice_with_cbor_template_cert_main.c" ]
   deps = [
+    ":dice_main",
     ":dice_with_cbor_template_cert",
-    ":utils",
   ]
 }
 
 pw_executable("dice_with_x509_template_cert_main") {
-  sources = [ "src/dice_with_x509_template_cert_main.c" ]
   deps = [
+    ":dice_main",
     ":dice_with_x509_template_cert",
-    ":utils",
   ]
 }
 
diff --git a/include/dice/boringssl_ops.h b/include/dice/boringssl_ops.h
deleted file mode 100644
index 410c41d..0000000
--- a/include/dice/boringssl_ops.h
+++ /dev/null
@@ -1,63 +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.
-
-#ifndef DICE_BORINGSSL_OPS_H_
-#define DICE_BORINGSSL_OPS_H_
-
-#include "dice/dice.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This is a DiceOps implementation which uses boringssl for crypto and
-// certificate generation. These functions are documented as part of the DiceOps
-// struct in dice.h. The algorithms used are SHA512, HKDF-SHA512, and
-// Ed25519-SHA512.
-DiceResult DiceBsslHashOp(const DiceOps* ops, const uint8_t* input,
-                          size_t input_size, uint8_t output[DICE_HASH_SIZE]);
-
-DiceResult DiceBsslKdfOp(const DiceOps* ops, 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);
-
-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],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // DICE_BORINGSSL_OPS_H_
diff --git a/include/dice/cbor_cert_op.h b/include/dice/cbor_cert_op.h
deleted file mode 100644
index 5bf5571..0000000
--- a/include/dice/cbor_cert_op.h
+++ /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.
-
-#ifndef DICE_CBOR_CERT_OP_H_
-#define DICE_CBOR_CERT_OP_H_
-
-#include "dice/dice.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// 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],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // DICE_CBOR_CERT_OP_H_
diff --git a/include/dice/dice.h b/include/dice/dice.h
index 647604f..2f4caa7 100644
--- a/include/dice/dice.h
+++ b/include/dice/dice.h
@@ -97,91 +97,17 @@
   uint8_t hidden[DICE_HIDDEN_SIZE];
 } DiceInputValues;
 
-// Contains a set of function pointers which implement various operations that
-// the DICE flow depends on.
-typedef struct DiceOps_ DiceOps;
-struct DiceOps_ {
-  // Available for use by the DiceOps implementation. May be null.
-  void* context;
-
-  // An implementation of SHA-512, or an alternative hash. Hashes |input_size|
-  // bytes of |input| and populates |output| on success.
-  DiceResult (*hash)(const DiceOps* ops, const uint8_t* input,
-                     size_t input_size, uint8_t output[DICE_HASH_SIZE]);
-
-  // An implementation of HKDF-SHA512, or an alternative KDF. Derives |length|
-  // bytes from |ikm|, |salt|, and |info| and populates |output| on success.
-  // |Output| must point to a buffer of at least |length| bytes.
-  DiceResult (*kdf)(const DiceOps* ops, 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);
-
-  // 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
-  // here so the implementation can choose between asymmetric mechanisms, for
-  // example ECDSA vs Ed25519.
-  DiceResult (*generate_certificate)(
-      const DiceOps* ops,
-      const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-      const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-      const DiceInputValues* input_values, size_t certificate_buffer_size,
-      uint8_t* certificate, size_t* certificate_actual_size);
-
-  // Securely clears |size| bytes at |address|. The default implementation uses
-  // the volatile data pointer approach which ostensibly works in practice, but
-  // a particular target platform or toolchain may have a better
-  // implementation available and if so, it can be plugged in here.
-  void (*clear_memory)(const DiceOps* ops, size_t size, void* address);
-};
-
 // Derives a |cdi_private_key_seed| from a |cdi_attest| value. On success
-// populates |cdi_private_key_seed| and returns kDiceResultOk. Note: of the
-// provided |ops|, only 'kdf' is called.
+// populates |cdi_private_key_seed| and returns kDiceResultOk.
 DiceResult DiceDeriveCdiPrivateKeySeed(
-    const DiceOps* ops, const uint8_t cdi_attest[DICE_CDI_SIZE],
+    void* context, const uint8_t cdi_attest[DICE_CDI_SIZE],
     uint8_t cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE]);
 
 // Derives an |id| from a |cdi_public_key| value. Because public keys can vary
 // in length depending on the algorithm, the |cdi_public_key_size| in bytes must
 // be provided. When interpreted as an integer, |id| is big-endian. On success
-// populates |id| and returns kDiceResultOk. Note: of the provided |ops|, only
-// 'kdf' is called.
-DiceResult DiceDeriveCdiCertificateId(const DiceOps* ops,
+// populates |id| and returns kDiceResultOk.
+DiceResult DiceDeriveCdiCertificateId(void* context,
                                       const uint8_t* cdi_public_key,
                                       size_t cdi_public_key_size,
                                       uint8_t id[20]);
@@ -193,7 +119,10 @@
 // specification for a detailed explanation of this flow.
 //
 // Parameters:
-//    ops: Operations provided by the caller.
+//    context: Context provided by the caller that is opaque to this library
+//        but is passed through to the integration-provided operations in
+//        dice/ops.h. The value is, therefore, integration-specific and may be
+//        null.
 //    current_cdi_attest, current_cdi_seal: The current CDI values as produced
 //        by a previous DICE flow. If this is the first DICE flow in a system,
 //        the Unique Device Secret (UDS) should be used for both of these
@@ -215,7 +144,7 @@
 //        attestation.
 //    next_cdi_seal: On success, will be populated with the next CDI value for
 //        sealing.
-DiceResult DiceMainFlow(const DiceOps* ops,
+DiceResult DiceMainFlow(void* context,
                         const uint8_t current_cdi_attest[DICE_CDI_SIZE],
                         const uint8_t current_cdi_seal[DICE_CDI_SIZE],
                         const DiceInputValues* input_values,
diff --git a/include/dice/fuzz_utils.h b/include/dice/fuzz_utils.h
deleted file mode 100644
index 89b1e22..0000000
--- a/include/dice/fuzz_utils.h
+++ /dev/null
@@ -1,26 +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 <stddef.h>
-#include <stdint.h>
-
-#include "dice/dice.h"
-
-namespace dice {
-namespace fuzz {
-
-int FuzzDiceMainFlow(const DiceOps* ops, const uint8_t* data, size_t size);
-
-}  // namespace fuzz
-}  // namespace dice
diff --git a/include/dice/mbedtls_ops.h b/include/dice/mbedtls_ops.h
deleted file mode 100644
index 3b71ab6..0000000
--- a/include/dice/mbedtls_ops.h
+++ /dev/null
@@ -1,48 +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.
-
-#ifndef DICE_MBEDTLS_OPS_H_
-#define DICE_MBEDTLS_OPS_H_
-
-#include "dice/dice.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This is a DiceOps implementation which uses mbedtls for crypto and
-// certificate generation. These functions are documented as part of the DiceOps
-// struct in dice.h. The algorithms used are SHA512, HKDF-SHA512, and
-// deterministic ECDSA-P256-SHA512.
-DiceResult DiceMbedtlsHashOp(const DiceOps* ops, const uint8_t* input,
-                             size_t input_size, uint8_t output[DICE_HASH_SIZE]);
-
-DiceResult DiceMbedtlsKdfOp(const DiceOps* ops, 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);
-
-DiceResult DiceMbedtlsGenerateCertificateOp(
-    const DiceOps* ops,
-    const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // DICE_MBEDTLS_OPS_H_
diff --git a/include/dice/ops.h b/include/dice/ops.h
new file mode 100644
index 0000000..cb3ee45
--- /dev/null
+++ b/include/dice/ops.h
@@ -0,0 +1,92 @@
+// 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_H_
+#define DICE_OPS_H_
+
+#include <dice/dice.h>
+
+// These are the set of functions that implement various operations that the
+// main DICE functions depend on. They are provided as part of an integration
+// and resolved at link time.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// An implementation of SHA-512, or an alternative hash. Hashes |input_size|
+// bytes of |input| and populates |output| on success.
+DiceResult DiceHash(void* context, const uint8_t* input, size_t input_size,
+                    uint8_t output[DICE_HASH_SIZE]);
+
+// An implementation of HKDF-SHA512, or an alternative KDF. Derives |length|
+// bytes from |ikm|, |salt|, and |info| and populates |output| on success.
+// |Output| must point to a buffer of at least |length| bytes.
+DiceResult DiceKdf(void* context, 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);
+
+// 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.
+DiceResult DiceKeypairFromSeed(void* context,
+                               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.
+DiceResult DiceSign(void* context, 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|.
+DiceResult DiceVerify(void* context, 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 here
+// so the implementation can choose between asymmetric mechanisms, for example
+// ECDSA vs Ed25519.
+DiceResult DiceGenerateCertificate(
+    void* context,
+    const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    const DiceInputValues* input_values, size_t certificate_buffer_size,
+    uint8_t* certificate, size_t* certificate_actual_size);
+
+// Securely clears |size| bytes at |address|. This project contains a basic
+// implementation. OPENSSL_cleanse from boringssl, SecureZeroMemory from
+// Windows and memset_s from C11 could also be used as an implementation but a
+// particular target platform or toolchain may have a better implementation
+// available that can be plugged in here. Care may be needed to ensure sensitive
+// data does not leak due to features such as caches.
+void DiceClearMemory(void* context, size_t size, void* address);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // DICE_OPS_H_
diff --git a/include/dice/template_cbor_cert_op.h b/include/dice/template_cbor_cert_op.h
deleted file mode 100644
index 284a054..0000000
--- a/include/dice/template_cbor_cert_op.h
+++ /dev/null
@@ -1,54 +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.
-
-#ifndef DICE_TEMPLATE_CBOR_CERT_OP_H_
-#define DICE_TEMPLATE_CBOR_CERT_OP_H_
-
-#include "dice/dice.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This function implements the 'DiceOps::generate_certificate' callback
-// documented in dice.h. It generates a CWT-style CBOR certificate based on a
-// 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. 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:
-//   * code_descriptor_size must be zero
-//   * authority_descriptor_size must be zero
-//   * config_type must be kDiceConfigTypeInline
-DiceResult DiceGenerateCborCertificateFromTemplateOp(
-    const DiceOps* ops,
-    const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // DICE_TEMPLATE_CBOR_CERT_OP_H_
diff --git a/include/dice/template_cert_op.h b/include/dice/template_cert_op.h
deleted file mode 100644
index da668f1..0000000
--- a/include/dice/template_cert_op.h
+++ /dev/null
@@ -1,52 +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.
-
-#ifndef DICE_TEMPLATE_CERT_OP_H_
-#define DICE_TEMPLATE_CERT_OP_H_
-
-#include "dice/dice.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This function implements the 'DiceOps::generate_certificate' callback
-// documented in dice.h. It generates an X.509 certificate based on a 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 an ASN.1
-// library. This implementation includes only hashes and inline configuration in
-// the DICE extension. For convenience this uses the lower level curve25519
-// implementation in boringssl but does not use anything else (no ASN.1, X.509,
-// etc). This approach may be especially useful in very low level components
-// where simplicity is paramount.
-//
-// This function will return kDiceResultInvalidInput if 'input_values' specifies
-// any variable length descriptors. In particular:
-//   * code_descriptor_size must be zero
-//   * authority_descriptor_size must be zero
-//   * config_type must be kDiceConfigTypeInline
-DiceResult DiceGenerateCertificateFromTemplateOp(
-    const DiceOps* ops,
-    const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // DICE_TEMPLATE_CERT_OP_H_
diff --git a/include/dice/test_utils.h b/include/dice/test_utils.h
index 841f44f..73383d4 100644
--- a/include/dice/test_utils.h
+++ b/include/dice/test_utils.h
@@ -49,7 +49,7 @@
 
 // Generates a self-signed X.509 UDS certificate for the given |uds| value. The
 // signature scheme is ED25519-SHA512.
-void CreateFakeUdsCertificate(const DiceOps& ops, const uint8_t uds[32],
+void CreateFakeUdsCertificate(void* context, const uint8_t uds[32],
                               CertificateType cert_type, KeyType key_type,
                               uint8_t certificate[kTestCertSize],
                               size_t* certificate_size);
diff --git a/include/dice/utils.h b/include/dice/utils.h
index 5778259..10e9c4b 100644
--- a/include/dice/utils.h
+++ b/include/dice/utils.h
@@ -31,9 +31,6 @@
 void DiceHexEncode(const uint8_t* in, size_t num_bytes, void* out,
                    size_t out_size);
 
-// A default implementation of DiceOps.clear_memory.
-void DiceClearMemory(const DiceOps* ops, size_t size, void* address);
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/boringssl_cert_op.c b/src/boringssl_cert_op.c
index cfac488..c4cd73a 100644
--- a/src/boringssl_cert_op.c
+++ b/src/boringssl_cert_op.c
@@ -12,10 +12,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+// This is a DiceGenerateCertificate implementation that uses boringssl for
+// crypto and certificate generation. The algorithms used are SHA512,
+// HKDF-SHA512, and Ed25519-SHA512.
+
 #include <stdint.h>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
+#include "dice/ops.h"
 #include "dice/utils.h"
 #include "openssl/asn1.h"
 #include "openssl/asn1t.h"
@@ -492,19 +496,19 @@
   return result;
 }
 
-static DiceResult GetIdFromKey(const DiceOps* ops, const EVP_PKEY* key,
+static DiceResult GetIdFromKey(void* context, const EVP_PKEY* key,
                                uint8_t id[20]) {
   uint8_t raw_public_key[32];
   size_t raw_public_key_size = sizeof(raw_public_key);
   if (!EVP_PKEY_get_raw_public_key(key, raw_public_key, &raw_public_key_size)) {
     return kDiceResultPlatformError;
   }
-  return DiceDeriveCdiCertificateId(ops, raw_public_key, raw_public_key_size,
-                                    id);
+  return DiceDeriveCdiCertificateId(context, raw_public_key,
+                                    raw_public_key_size, id);
 }
 
-DiceResult DiceBsslGenerateCertificateOp(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
@@ -541,12 +545,12 @@
   }
 
   uint8_t authority_id[20];
-  result = GetIdFromKey(ops, authority_key, authority_id);
+  result = GetIdFromKey(context, authority_key, authority_id);
   if (result != kDiceResultOk) {
     goto out;
   }
   uint8_t subject_id[20];
-  result = GetIdFromKey(ops, subject_key, subject_id);
+  result = GetIdFromKey(context, subject_key, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
diff --git a/src/boringssl_hash_kdf_sign_ops.c b/src/boringssl_hash_kdf_sign_ops.c
index a1cf5a9..c135dae 100644
--- a/src/boringssl_hash_kdf_sign_ops.c
+++ b/src/boringssl_hash_kdf_sign_ops.c
@@ -12,29 +12,30 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+// This is an implementation of the crypto operations that uses boringssl. The
+// algorithms used are SHA512, HKDF-SHA512, and Ed25519-SHA512.
+
 #include <stdint.h>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
+#include "dice/ops.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;
+DiceResult DiceHash(void* context_not_used, const uint8_t* input,
+                    size_t input_size, uint8_t output[DICE_HASH_SIZE]) {
+  (void)context_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;
+DiceResult DiceKdf(void* context_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)context_not_used;
   if (!HKDF(output, length, EVP_sha512(), ikm, ikm_size, salt, salt_size, info,
             info_size)) {
     return kDiceResultPlatformError;
@@ -42,11 +43,13 @@
   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;
+DiceResult DiceKeypairFromSeed(void* context_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)context_not_used;
 #if DICE_PRIVATE_KEY_SEED_SIZE != 32
 #error "Private key seed is expected to be 32 bytes."
 #endif
@@ -62,12 +65,11 @@
   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;
+DiceResult DiceSign(void* context_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)context_not_used;
   if (private_key_size != 64 || signature_size != 64) {
     return kDiceResultPlatformError;
   }
@@ -77,13 +79,11 @@
   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;
+DiceResult DiceVerify(void* context_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)context_not_used;
   if (public_key_size != 32 || signature_size != 64) {
     return kDiceResultPlatformError;
   }
diff --git a/src/boringssl_ops_fuzzer.cc b/src/boringssl_ops_fuzzer.cc
deleted file mode 100644
index 643a834..0000000
--- a/src/boringssl_ops_fuzzer.cc
+++ /dev/null
@@ -1,33 +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 "dice/boringssl_ops.h"
-#include "dice/dice.h"
-#include "dice/fuzz_utils.h"
-#include "dice/utils.h"
-
-namespace {
-
-constexpr DiceOps kOps = {.context = NULL,
-                          .hash = DiceBsslHashOp,
-                          .kdf = DiceBsslKdfOp,
-                          .generate_certificate = DiceBsslGenerateCertificateOp,
-                          .clear_memory = DiceClearMemory};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  dice::fuzz::FuzzDiceMainFlow(&kOps, data, size);
-  return 0;
-}
diff --git a/src/boringssl_ops_test.cc b/src/boringssl_ops_test.cc
index 9dc94ba..971ad0b 100644
--- a/src/boringssl_ops_test.cc
+++ b/src/boringssl_ops_test.cc
@@ -12,8 +12,6 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/boringssl_ops.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -35,18 +33,12 @@
 using dice::test::DumpState;
 using dice::test::KeyType_Ed25519;
 
-constexpr DiceOps kOps = {.context = NULL,
-                          .hash = DiceBsslHashOp,
-                          .kdf = DiceBsslKdfOp,
-                          .generate_certificate = DiceBsslGenerateCertificateOp,
-                          .clear_memory = DiceClearMemory};
-
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -76,7 +68,7 @@
                        input_values.config_value);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -125,7 +117,7 @@
   input_values.authority_descriptor_size = sizeof(authority_descriptor);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -151,7 +143,7 @@
   DiceInputValues input_values = {};
   input_values.mode = kDiceModeDebug;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -166,7 +158,7 @@
   input_values.code_descriptor = kBigBuffer;
   input_values.code_descriptor_size = sizeof(kBigBuffer);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -178,7 +170,7 @@
   DiceInputValues input_values = {};
   input_values.config_type = (DiceConfigType)55;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -200,7 +192,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -230,7 +222,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -242,7 +234,7 @@
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      kOps, states[0].cdi_attest, CertificateType_X509, KeyType_Ed25519,
+      NULL, states[0].cdi_attest, CertificateType_X509, KeyType_Ed25519,
       root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
       CertificateType_X509, root_certificate, root_certificate_size, &states[1],
diff --git a/src/cbor_cert_op.c b/src/cbor_cert_op.c
index c3e6c91..a49947f 100644
--- a/src/cbor_cert_op.c
+++ b/src/cbor_cert_op.c
@@ -12,7 +12,8 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/cbor_cert_op.h"
+// This is a DiceGenerateCertificate implementation that generates a CWT-style
+// CBOR certificate using the ED25519-SHA512 signature scheme.
 
 #include <stddef.h>
 #include <stdint.h>
@@ -20,6 +21,7 @@
 
 #include "dice/cbor_writer.h"
 #include "dice/dice.h"
+#include "dice/ops.h"
 #include "dice/utils.h"
 
 // Max size of COSE_Sign1 including payload.
@@ -90,8 +92,7 @@
 
 // Encodes a CBOR Web Token (CWT) with an issuer, subject, and additional
 // fields.
-static DiceResult EncodeCwt(const DiceOps* ops,
-                            const DiceInputValues* input_values,
+static DiceResult EncodeCwt(void* context, const DiceInputValues* input_values,
                             const char* authority_id_hex,
                             const char* subject_id_hex,
                             const uint8_t* encoded_public_key,
@@ -149,8 +150,8 @@
   if (input_values->config_type == kDiceConfigTypeDescriptor) {
     uint8_t config_descriptor_hash[DICE_HASH_SIZE];
     DiceResult result =
-        ops->hash(ops, input_values->config_descriptor,
-                  input_values->config_descriptor_size, config_descriptor_hash);
+        DiceHash(context, input_values->config_descriptor,
+                 input_values->config_descriptor_size, config_descriptor_hash);
     if (result != kDiceResultOk) {
       return result;
     }
@@ -241,8 +242,8 @@
   return kDiceResultOk;
 }
 
-DiceResult DiceGenerateCborCertificateOp(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
@@ -268,15 +269,16 @@
   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);
+  result = DiceKeypairFromSeed(context, 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(context, subject_public_key, 32, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -288,16 +290,16 @@
   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);
+  result =
+      DiceKeypairFromSeed(context, 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,
+  result = DiceDeriveCdiCertificateId(context, authority_public_key,
                                       authority_public_key_size, authority_id);
   if (result != kDiceResultOk) {
     goto out;
@@ -327,7 +329,7 @@
 
   // The CWT is the payload in both the TBS and the final COSE_Sign1 structure.
   size_t payload_size = 0;
-  result = EncodeCwt(ops, input_values, authority_id_hex, subject_id_hex,
+  result = EncodeCwt(context, input_values, authority_id_hex, subject_id_hex,
                      encoded_public_key, encoded_public_key_size,
                      sizeof(payload), payload, &payload_size);
   if (result != kDiceResultOk) {
@@ -345,19 +347,17 @@
 
   // Sign the TBS with the authority key.
   uint8_t signature[64];
-  result = ops->sign(ops, certificate, *certificate_actual_size,
-                     authority_private_key, authority_private_key_size,
-                     sizeof(signature), signature);
+  result = DiceSign(context, certificate, *certificate_actual_size,
+                    authority_private_key, authority_private_key_size,
+                    sizeof(signature), signature);
   if (result != kDiceResultOk) {
     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;
-    }
+  result = DiceVerify(context, 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.
@@ -366,8 +366,9 @@
       signature, certificate_buffer_size, certificate, certificate_actual_size);
 
 out:
-  ops->clear_memory(ops, sizeof(subject_private_key), subject_private_key);
-  ops->clear_memory(ops, sizeof(authority_private_key), authority_private_key);
+  DiceClearMemory(context, sizeof(subject_private_key), subject_private_key);
+  DiceClearMemory(context, sizeof(authority_private_key),
+                  authority_private_key);
 
   return result;
 }
diff --git a/src/cbor_cert_op_fuzzer.cc b/src/cbor_cert_op_fuzzer.cc
deleted file mode 100644
index e8631d4..0000000
--- a/src/cbor_cert_op_fuzzer.cc
+++ /dev/null
@@ -1,37 +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 "dice/boringssl_ops.h"
-#include "dice/cbor_cert_op.h"
-#include "dice/dice.h"
-#include "dice/fuzz_utils.h"
-#include "dice/utils.h"
-
-namespace {
-
-constexpr DiceOps kOps = {.context = NULL,
-                          .hash = DiceBsslHashOp,
-                          .kdf = DiceBsslKdfOp,
-                          .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
-                          .sign = DiceBsslEd25519Sign,
-                          .verify = DiceBsslEd25519Verify,
-                          .generate_certificate = DiceGenerateCborCertificateOp,
-                          .clear_memory = DiceClearMemory};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  dice::fuzz::FuzzDiceMainFlow(&kOps, data, size);
-  return 0;
-}
diff --git a/src/cbor_cert_op_test.cc b/src/cbor_cert_op_test.cc
index e424509..cb41c5c 100644
--- a/src/cbor_cert_op_test.cc
+++ b/src/cbor_cert_op_test.cc
@@ -12,15 +12,12 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/cbor_cert_op.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include <memory>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
 #include "dice/known_test_values.h"
 #include "dice/test_framework.h"
@@ -35,23 +32,12 @@
 using dice::test::DiceStateForTest;
 using dice::test::KeyType_Ed25519;
 
-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 = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -81,7 +67,7 @@
                        input_values.config_value);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -130,7 +116,7 @@
   input_values.authority_descriptor_size = sizeof(authority_descriptor);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -156,7 +142,7 @@
   DiceInputValues input_values = {};
   input_values.mode = kDiceModeDebug;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -171,7 +157,7 @@
   input_values.code_descriptor = kBigBuffer;
   input_values.code_descriptor_size = sizeof(kBigBuffer);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -183,7 +169,7 @@
   DiceInputValues input_values = {};
   input_values.config_type = (DiceConfigType)55;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -205,7 +191,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -235,7 +221,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -247,7 +233,7 @@
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      kOps, states[0].cdi_attest, CertificateType_Cbor, KeyType_Ed25519,
+      NULL, states[0].cdi_attest, CertificateType_Cbor, KeyType_Ed25519,
       root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
       CertificateType_Cbor, root_certificate, root_certificate_size, &states[1],
diff --git a/src/clear_memory.c b/src/clear_memory.c
new file mode 100644
index 0000000..0fdc7cf
--- /dev/null
+++ b/src/clear_memory.c
@@ -0,0 +1,28 @@
+// 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.
+
+// This is a basic, standalone implementation of DiceClearMemory that aims to
+// write zeros to the memory without the compiler optimizing it away by using a
+// volatile data pointer. Attention has not been given to performance, clearing
+// caches or other potential side channels.
+
+#include "dice/ops.h"
+
+void DiceClearMemory(void* context, size_t size, void* address) {
+  (void)context;
+  volatile uint8_t* p = address;
+  for (size_t i = 0; i < size; i++) {
+    p[i] = 0;
+  }
+}
diff --git a/src/dice.c b/src/dice.c
index 23548e9..8edc02b 100644
--- a/src/dice.c
+++ b/src/dice.c
@@ -16,6 +16,8 @@
 
 #include <string.h>
 
+#include "dice/ops.h"
+
 static const uint8_t kAsymSalt[] = {
     0x63, 0xB6, 0xA0, 0x4D, 0x2C, 0x07, 0x7F, 0xC1, 0x0F, 0x63, 0x9F,
     0x21, 0xDA, 0x79, 0x38, 0x44, 0x35, 0x6C, 0xC2, 0xB0, 0xB4, 0x41,
@@ -35,24 +37,24 @@
 static const size_t kIdSaltSize = 64;
 
 DiceResult DiceDeriveCdiPrivateKeySeed(
-    const DiceOps* ops, const uint8_t cdi_attest[DICE_CDI_SIZE],
+    void* context, const uint8_t cdi_attest[DICE_CDI_SIZE],
     uint8_t cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE]) {
   // Use the CDI as input key material, with fixed salt and info.
-  return ops->kdf(ops, /*length=*/DICE_PRIVATE_KEY_SEED_SIZE, cdi_attest,
-                  /*ikm_size=*/DICE_CDI_SIZE, kAsymSalt, kAsymSaltSize,
-                  /*info=*/(const uint8_t*)"Key Pair", /*info_size=*/8,
-                  cdi_private_key_seed);
+  return DiceKdf(context, /*length=*/DICE_PRIVATE_KEY_SEED_SIZE, cdi_attest,
+                 /*ikm_size=*/DICE_CDI_SIZE, kAsymSalt, kAsymSaltSize,
+                 /*info=*/(const uint8_t*)"Key Pair", /*info_size=*/8,
+                 cdi_private_key_seed);
 }
 
-DiceResult DiceDeriveCdiCertificateId(const DiceOps* ops,
+DiceResult DiceDeriveCdiCertificateId(void* context,
                                       const uint8_t* cdi_public_key,
                                       size_t cdi_public_key_size,
                                       uint8_t id[20]) {
   // Use the public key as input key material, with fixed salt and info.
   DiceResult result =
-      ops->kdf(ops, /*length=*/20, cdi_public_key, cdi_public_key_size, kIdSalt,
-               kIdSaltSize,
-               /*info=*/(const uint8_t*)"ID", /*info_size=*/2, id);
+      DiceKdf(context, /*length=*/20, cdi_public_key, cdi_public_key_size,
+              kIdSalt, kIdSaltSize,
+              /*info=*/(const uint8_t*)"ID", /*info_size=*/2, id);
   if (result == kDiceResultOk) {
     // Clear the top bit to keep the integer positive.
     id[0] &= ~0x80;
@@ -60,7 +62,7 @@
   return result;
 }
 
-DiceResult DiceMainFlow(const DiceOps* ops,
+DiceResult DiceMainFlow(void* context,
                         const uint8_t current_cdi_attest[DICE_CDI_SIZE],
                         const uint8_t current_cdi_seal[DICE_CDI_SIZE],
                         const DiceInputValues* input_values,
@@ -105,9 +107,9 @@
     result = kDiceResultInvalidInput;
     goto out;
   } else {
-    result = ops->hash(ops, input_values->config_descriptor,
-                       input_values->config_descriptor_size,
-                       &input_buffer[kConfigOffset]);
+    result = DiceHash(context, input_values->config_descriptor,
+                      input_values->config_descriptor_size,
+                      &input_buffer[kConfigOffset]);
     if (result != kDiceResultOk) {
       goto out;
     }
@@ -121,28 +123,28 @@
   // attestation all the inputs are used, and for sealing only the authority,
   // mode, and hidden inputs are used.
   result =
-      ops->hash(ops, input_buffer, sizeof(input_buffer), attest_input_hash);
+      DiceHash(context, input_buffer, sizeof(input_buffer), attest_input_hash);
   if (result != kDiceResultOk) {
     goto out;
   }
-  result = ops->hash(ops, &input_buffer[kAuthorityOffset],
-                     kAuthoritySize + kModeSize + kHiddenSize, seal_input_hash);
+  result = DiceHash(context, &input_buffer[kAuthorityOffset],
+                    kAuthoritySize + kModeSize + kHiddenSize, seal_input_hash);
   if (result != kDiceResultOk) {
     goto out;
   }
 
   // Compute the next CDI values. For each of these the current CDI value is
   // used as input key material and the input hash is used as salt.
-  result = ops->kdf(ops, /*length=*/DICE_CDI_SIZE, current_cdi_attest,
-                    /*ikm_size=*/DICE_CDI_SIZE, attest_input_hash,
-                    /*salt_size=*/DICE_HASH_SIZE,
-                    /*info=*/(const uint8_t*)"CDI_Attest", /*info_size=*/10,
-                    next_cdi_attest);
+  result = DiceKdf(context, /*length=*/DICE_CDI_SIZE, current_cdi_attest,
+                   /*ikm_size=*/DICE_CDI_SIZE, attest_input_hash,
+                   /*salt_size=*/DICE_HASH_SIZE,
+                   /*info=*/(const uint8_t*)"CDI_Attest", /*info_size=*/10,
+                   next_cdi_attest);
   if (result != kDiceResultOk) {
     goto out;
   }
-  result = ops->kdf(
-      ops, /*length=*/DICE_CDI_SIZE, current_cdi_seal,
+  result = DiceKdf(
+      context, /*length=*/DICE_CDI_SIZE, current_cdi_seal,
       /*ikm_size=*/DICE_CDI_SIZE, seal_input_hash, /*salt_size=*/DICE_HASH_SIZE,
       /*info=*/(const uint8_t*)"CDI_Seal", /*info_size=*/8, next_cdi_seal);
   if (result != kDiceResultOk) {
@@ -150,12 +152,12 @@
   }
 
   // Derive asymmetric private key seeds from the attestation CDI values.
-  result = DiceDeriveCdiPrivateKeySeed(ops, current_cdi_attest,
+  result = DiceDeriveCdiPrivateKeySeed(context, current_cdi_attest,
                                        current_cdi_private_key_seed);
   if (result != kDiceResultOk) {
     goto out;
   }
-  result = DiceDeriveCdiPrivateKeySeed(ops, next_cdi_attest,
+  result = DiceDeriveCdiPrivateKeySeed(context, next_cdi_attest,
                                        next_cdi_private_key_seed);
   if (result != kDiceResultOk) {
     goto out;
@@ -163,8 +165,8 @@
 
   // Generate a certificate for |next_cdi_private_key_seed| with
   // |current_cdi_private_key_seed| as the authority.
-  result = ops->generate_certificate(
-      ops, next_cdi_private_key_seed, current_cdi_private_key_seed,
+  result = DiceGenerateCertificate(
+      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) {
@@ -172,12 +174,12 @@
   }
 out:
   // Clear sensitive memory.
-  ops->clear_memory(ops, sizeof(input_buffer), input_buffer);
-  ops->clear_memory(ops, sizeof(attest_input_hash), attest_input_hash);
-  ops->clear_memory(ops, sizeof(seal_input_hash), seal_input_hash);
-  ops->clear_memory(ops, sizeof(current_cdi_private_key_seed),
-                    current_cdi_private_key_seed);
-  ops->clear_memory(ops, sizeof(next_cdi_private_key_seed),
-                    next_cdi_private_key_seed);
+  DiceClearMemory(context, sizeof(input_buffer), input_buffer);
+  DiceClearMemory(context, sizeof(attest_input_hash), attest_input_hash);
+  DiceClearMemory(context, sizeof(seal_input_hash), seal_input_hash);
+  DiceClearMemory(context, sizeof(current_cdi_private_key_seed),
+                  current_cdi_private_key_seed);
+  DiceClearMemory(context, sizeof(next_cdi_private_key_seed),
+                  next_cdi_private_key_seed);
   return result;
 }
diff --git a/src/dice_with_x509_template_cert_main.c b/src/dice_main.c
similarity index 67%
rename from src/dice_with_x509_template_cert_main.c
rename to src/dice_main.c
index 2cc16b5..eebbb7b 100644
--- a/src/dice_with_x509_template_cert_main.c
+++ b/src/dice_main.c
@@ -14,23 +14,17 @@
 
 #include <stdint.h>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
-#include "dice/template_cert_op.h"
 #include "dice/utils.h"
 
 int main(int argc, char** argv) {
   (void)argc;
   (void)argv;
-  const DiceOps ops = {
-      .hash = DiceBsslHashOp,
-      .kdf = DiceBsslKdfOp,
-      .generate_certificate = DiceGenerateCertificateFromTemplateOp,
-      .clear_memory = DiceClearMemory};
   uint8_t cdi_buffer[DICE_CDI_SIZE];
   uint8_t cert_buffer[2048];
   size_t cert_size;
   DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
+  return (int)DiceMainFlow(/*context=*/NULL, cdi_buffer, cdi_buffer,
+                           &input_values, sizeof(cert_buffer), cert_buffer,
+                           &cert_size, cdi_buffer, cdi_buffer);
 }
diff --git a/src/dice_standalone_main.c b/src/dice_standalone_main.c
index caa4814..7f7b5a2 100644
--- a/src/dice_standalone_main.c
+++ b/src/dice_standalone_main.c
@@ -17,19 +17,19 @@
 #include "dice/dice.h"
 #include "dice/utils.h"
 
-DiceResult FakeHash(const DiceOps* ops, const uint8_t* input, size_t input_size,
+DiceResult DiceHash(void* context, const uint8_t* input, size_t input_size,
                     uint8_t output[DICE_HASH_SIZE]) {
-  (void)ops;
+  (void)context;
   (void)input;
   (void)input_size;
   (void)output;
   return kDiceResultOk;
 }
 
-DiceResult FakeKdf(const DiceOps* ops, size_t length, const uint8_t* ikm,
+DiceResult DiceKdf(void* context, 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;
+  (void)context;
   (void)length;
   (void)ikm;
   (void)ikm_size;
@@ -41,13 +41,13 @@
   return kDiceResultOk;
 }
 
-DiceResult FakeCertificate(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
     uint8_t* certificate, size_t* certificate_actual_size) {
-  (void)ops;
+  (void)context;
   (void)subject_private_key_seed;
   (void)authority_private_key_seed;
   (void)input_values;
@@ -60,14 +60,11 @@
 int main(int argc, char** argv) {
   (void)argc;
   (void)argv;
-  const DiceOps ops = {.hash = FakeHash,
-                       .kdf = FakeKdf,
-                       .generate_certificate = FakeCertificate,
-                       .clear_memory = DiceClearMemory};
   uint8_t cdi_buffer[DICE_CDI_SIZE];
   uint8_t cert_buffer[2048];
   size_t cert_size;
   DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
+  return (int)DiceMainFlow(/*context=*/NULL, cdi_buffer, cdi_buffer,
+                           &input_values, sizeof(cert_buffer), cert_buffer,
+                           &cert_size, cdi_buffer, cdi_buffer);
 }
diff --git a/src/dice_test.cc b/src/dice_test.cc
index 802896d..65330f7 100644
--- a/src/dice_test.cc
+++ b/src/dice_test.cc
@@ -24,27 +24,10 @@
 
 namespace {
 
-extern "C" {
-DiceResult FakeHash(const DiceOps* ops, const uint8_t* input, size_t input_size,
-                    uint8_t output[DICE_HASH_SIZE]);
-
-DiceResult FakeKdf(const DiceOps* ops, 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);
-
-DiceResult FakeGenerateCertificate(
-    const DiceOps* ops,
-    const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
-    const DiceInputValues* input_values, size_t certificate_buffer_size,
-    uint8_t* certificate, size_t* certificate_actual_size);
-}  // extern "C"
-
 const size_t kFakeCertSize = 200;
 
 struct FakeDiceOps {
   FakeDiceOps() { CRYPTO_library_init(); }
-  operator const DiceOps*() const { return &ops_; }
 
   // DiceOps calls to |hash| forward here.
   DiceResult Hash(const uint8_t* input, size_t input_size,
@@ -93,39 +76,33 @@
   int hash_count_ = 0;
   int kdf_count_ = 0;
   int generate_certificate_count_ = 0;
-
-  // This is used as the DiceOps argument for DiceMainFlow calls.
-  DiceOps ops_ = {.context = this,
-                  .hash = FakeHash,
-                  .kdf = FakeKdf,
-                  .generate_certificate = FakeGenerateCertificate,
-                  .clear_memory = DiceClearMemory};
 };
 
+extern "C" {
 // These callbacks forward to a FakeDiceOps instance.
-DiceResult FakeHash(const DiceOps* ops, const uint8_t* input, size_t input_size,
+DiceResult DiceHash(void* context, const uint8_t* input, size_t input_size,
                     uint8_t output[DICE_HASH_SIZE]) {
-  return reinterpret_cast<FakeDiceOps*>(ops->context)
-      ->Hash(input, input_size, output);
+  return reinterpret_cast<FakeDiceOps*>(context)->Hash(input, input_size,
+                                                       output);
 }
 
-DiceResult FakeKdf(const DiceOps* ops, size_t length, const uint8_t* ikm,
+DiceResult DiceKdf(void* context, 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) {
-  return reinterpret_cast<FakeDiceOps*>(ops->context)
-      ->Kdf(length, ikm, ikm_size, salt, salt_size, info, info_size, output);
+  return reinterpret_cast<FakeDiceOps*>(context)->Kdf(
+      length, ikm, ikm_size, salt, salt_size, info, info_size, output);
 }
 
-DiceResult FakeGenerateCertificate(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
     uint8_t* certificate, size_t* certificate_actual_size) {
-  return reinterpret_cast<FakeDiceOps*>(ops->context)
-      ->GenerateCertificate(
-          subject_private_key_seed, authority_private_key_seed, input_values,
-          certificate_buffer_size, certificate, certificate_actual_size);
+  return reinterpret_cast<FakeDiceOps*>(context)->GenerateCertificate(
+      subject_private_key_seed, authority_private_key_seed, input_values,
+      certificate_buffer_size, certificate, certificate_actual_size);
+}
 }
 
 struct DiceStateForTest {
@@ -141,7 +118,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -159,7 +136,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &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);
   EXPECT_EQ(kDiceResultPlatformError, result);
@@ -172,7 +149,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &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);
   EXPECT_EQ(kDiceResultPlatformError, result);
@@ -185,7 +162,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &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);
   EXPECT_EQ(kDiceResultPlatformError, result);
@@ -197,7 +174,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
       kFakeCertSize - 1, next_state.certificate, &next_state.certificate_size,
       next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -210,7 +187,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      &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);
   EXPECT_EQ(kDiceResultOk, result);
diff --git a/src/dice_with_boringssl_ops_main.c b/src/dice_with_boringssl_ops_main.c
deleted file mode 100644
index 3906117..0000000
--- a/src/dice_with_boringssl_ops_main.c
+++ /dev/null
@@ -1,34 +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 "dice/utils.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (void)argv;
-  const DiceOps ops = {.hash = DiceBsslHashOp,
-                       .kdf = DiceBsslKdfOp,
-                       .generate_certificate = DiceBsslGenerateCertificateOp,
-                       .clear_memory = DiceClearMemory};
-  uint8_t cdi_buffer[DICE_CDI_SIZE];
-  uint8_t cert_buffer[2048];
-  size_t cert_size;
-  DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
-}
diff --git a/src/dice_with_cbor_cert_main.c b/src/dice_with_cbor_cert_main.c
deleted file mode 100644
index a0cd67e..0000000
--- a/src/dice_with_cbor_cert_main.c
+++ /dev/null
@@ -1,38 +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/cbor_cert_op.h"
-#include "dice/dice.h"
-#include "dice/utils.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (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];
-  uint8_t cert_buffer[2048];
-  size_t cert_size;
-  DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
-}
diff --git a/src/dice_with_cbor_template_cert_main.c b/src/dice_with_cbor_template_cert_main.c
deleted file mode 100644
index bb131d2..0000000
--- a/src/dice_with_cbor_template_cert_main.c
+++ /dev/null
@@ -1,39 +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 "dice/template_cbor_cert_op.h"
-#include "dice/utils.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (void)argv;
-  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];
-  uint8_t cert_buffer[2048];
-  size_t cert_size;
-  DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
-}
diff --git a/src/dice_with_mbedtls_ops_main.c b/src/dice_with_mbedtls_ops_main.c
deleted file mode 100644
index e130a7b..0000000
--- a/src/dice_with_mbedtls_ops_main.c
+++ /dev/null
@@ -1,34 +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/dice.h"
-#include "dice/mbedtls_ops.h"
-#include "dice/utils.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (void)argv;
-  const DiceOps ops = {.hash = DiceMbedtlsHashOp,
-                       .kdf = DiceMbedtlsKdfOp,
-                       .generate_certificate = DiceMbedtlsGenerateCertificateOp,
-                       .clear_memory = DiceClearMemory};
-  uint8_t cdi_buffer[DICE_CDI_SIZE];
-  uint8_t cert_buffer[2048];
-  size_t cert_size;
-  DiceInputValues input_values = {0};
-  return (int)DiceMainFlow(&ops, cdi_buffer, cdi_buffer, &input_values, 2048,
-                           cert_buffer, &cert_size, cdi_buffer, cdi_buffer);
-}
diff --git a/src/fuzz_utils.cc b/src/fuzzer.cc
similarity index 88%
rename from src/fuzz_utils.cc
rename to src/fuzzer.cc
index b5cce1b..8acb01e 100644
--- a/src/fuzz_utils.cc
+++ b/src/fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 Google LLC
+// 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
@@ -12,9 +12,8 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/fuzz_utils.h"
-
 #include "dice/dice.h"
+#include "dice/utils.h"
 #include "fuzzer/FuzzedDataProvider.h"
 
 namespace {
@@ -68,10 +67,7 @@
 
 }  // namespace
 
-namespace dice {
-namespace fuzz {
-
-int FuzzDiceMainFlow(const DiceOps* ops, const uint8_t* data, size_t size) {
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   // Exit early if there might not be enough data to fill buffers.
   if (size < 512) {
     return 0;
@@ -100,13 +96,9 @@
   fdp.ConsumeData(&next_cdi_seal, DICE_CDI_SIZE);
 
   // Fuzz the main flow.
-  DiceMainFlow(ops, current_cdi_attest, current_cdi_seal, input_values,
-               kNextCdiCertificateBufferSize, next_cdi_certificate,
-               &next_cdi_certificate_actual_size, next_cdi_attest,
-               next_cdi_seal);
-
+  DiceMainFlow(/*context=*/NULL, current_cdi_attest, current_cdi_seal,
+               input_values, kNextCdiCertificateBufferSize,
+               next_cdi_certificate, &next_cdi_certificate_actual_size,
+               next_cdi_attest, next_cdi_seal);
   return 0;
 }
-
-}  // namespace fuzz
-}  // namespace dice
diff --git a/src/mbedtls_ops.c b/src/mbedtls_ops.c
index a8e3151..de928d7 100644
--- a/src/mbedtls_ops.c
+++ b/src/mbedtls_ops.c
@@ -12,12 +12,15 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/mbedtls_ops.h"
+// This is an implementation of DiceGenerateCertificate and the crypto
+// operations that uses mbedtls. The algorithms used are SHA512, HKDF-SHA512,
+// and deterministic ECDSA-P256-SHA512.
 
 #include <stdint.h>
 #include <string.h>
 
 #include "dice/dice.h"
+#include "dice/ops.h"
 #include "dice/utils.h"
 #include "mbedtls/asn1.h"
 #include "mbedtls/asn1write.h"
@@ -67,20 +70,20 @@
   return result;
 }
 
-static DiceResult GetIdFromKey(const DiceOps* ops,
-                               const mbedtls_pk_context* context,
+static DiceResult GetIdFromKey(void* context,
+                               const mbedtls_pk_context* pk_context,
                                uint8_t id[20]) {
   uint8_t raw_public_key[33];
   size_t raw_public_key_size = 0;
-  mbedtls_ecp_keypair* key = mbedtls_pk_ec(*context);
+  mbedtls_ecp_keypair* key = mbedtls_pk_ec(*pk_context);
 
   if (0 != mbedtls_ecp_point_write_binary(
                &key->grp, &key->Q, MBEDTLS_ECP_PF_COMPRESSED,
                &raw_public_key_size, raw_public_key, sizeof(raw_public_key))) {
     return kDiceResultPlatformError;
   }
-  return DiceDeriveCdiCertificateId(ops, raw_public_key, raw_public_key_size,
-                                    id);
+  return DiceDeriveCdiCertificateId(context, raw_public_key,
+                                    raw_public_key_size, id);
 }
 
 // 54 byte name is prefix (13), hex id (40), and a null terminator.
@@ -267,10 +270,9 @@
   return kDiceResultOk;
 }
 
-DiceResult DiceMbedtlsHashOp(const DiceOps* ops_not_used, const uint8_t* input,
-                             size_t input_size,
-                             uint8_t output[DICE_HASH_SIZE]) {
-  (void)ops_not_used;
+DiceResult DiceHash(void* context_not_used, const uint8_t* input,
+                    size_t input_size, uint8_t output[DICE_HASH_SIZE]) {
+  (void)context_not_used;
   if (0 != mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), input,
                       input_size, output)) {
     return kDiceResultPlatformError;
@@ -278,12 +280,10 @@
   return kDiceResultOk;
 }
 
-DiceResult DiceMbedtlsKdfOp(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;
+DiceResult DiceKdf(void* context_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)context_not_used;
   if (0 != mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), salt,
                         salt_size, ikm, ikm_size, info, info_size, output,
                         length)) {
@@ -292,8 +292,8 @@
   return kDiceResultOk;
 }
 
-DiceResult DiceMbedtlsGenerateCertificateOp(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
@@ -331,7 +331,7 @@
   }
 
   uint8_t authority_id[20];
-  result = GetIdFromKey(ops, &authority_key_context, authority_id);
+  result = GetIdFromKey(context, &authority_key_context, authority_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -351,7 +351,7 @@
   }
 
   uint8_t subject_id[20];
-  result = GetIdFromKey(ops, &subject_key_context, subject_id);
+  result = GetIdFromKey(context, &subject_key_context, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
diff --git a/src/mbedtls_ops_fuzzer.cc b/src/mbedtls_ops_fuzzer.cc
deleted file mode 100644
index 5621aea..0000000
--- a/src/mbedtls_ops_fuzzer.cc
+++ /dev/null
@@ -1,34 +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 "dice/dice.h"
-#include "dice/fuzz_utils.h"
-#include "dice/mbedtls_ops.h"
-#include "dice/utils.h"
-
-namespace {
-
-constexpr DiceOps kOps = {
-    .context = NULL,
-    .hash = DiceMbedtlsHashOp,
-    .kdf = DiceMbedtlsKdfOp,
-    .generate_certificate = DiceMbedtlsGenerateCertificateOp,
-    .clear_memory = DiceClearMemory};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  dice::fuzz::FuzzDiceMainFlow(&kOps, data, size);
-  return 0;
-}
diff --git a/src/mbedtls_ops_test.cc b/src/mbedtls_ops_test.cc
index 9b8c618..ff54180 100644
--- a/src/mbedtls_ops_test.cc
+++ b/src/mbedtls_ops_test.cc
@@ -12,8 +12,6 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/mbedtls_ops.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -35,19 +33,12 @@
 using dice::test::DumpState;
 using dice::test::KeyType_P256;
 
-constexpr DiceOps kOps = {
-    .context = nullptr,
-    .hash = DiceMbedtlsHashOp,
-    .kdf = DiceMbedtlsKdfOp,
-    .generate_certificate = DiceMbedtlsGenerateCertificateOp,
-    .clear_memory = DiceClearMemory};
-
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -77,7 +68,7 @@
                        input_values.config_value);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -125,7 +116,7 @@
   input_values.authority_descriptor_size = sizeof(authority_descriptor);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -150,7 +141,7 @@
   DiceInputValues input_values = {};
   input_values.mode = kDiceModeDebug;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -165,7 +156,7 @@
   input_values.code_descriptor = kBigBuffer;
   input_values.code_descriptor_size = sizeof(kBigBuffer);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -177,7 +168,7 @@
   DiceInputValues input_values = {};
   input_values.config_type = (DiceConfigType)55;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -199,7 +190,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -229,7 +220,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -241,7 +232,7 @@
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      kOps, states[0].cdi_attest, dice::test::CertificateType_X509,
+      NULL, states[0].cdi_attest, dice::test::CertificateType_X509,
       dice::test::KeyType_P256, root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
       CertificateType_X509, root_certificate, root_certificate_size, &states[1],
diff --git a/src/template_cbor_cert_op.c b/src/template_cbor_cert_op.c
index 96c300b..21aa186 100644
--- a/src/template_cbor_cert_op.c
+++ b/src/template_cbor_cert_op.c
@@ -19,10 +19,27 @@
 // implementation in boringssl. This approach may be especially useful in very
 // low level components where simplicity is paramount.
 
+// This is an implementation of the DiceGenerateCertificate that generates a
+// CWT-style CBOR certificate based on a 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. This approach may be especially
+// useful in very low level components where simplicity is paramount.
+//
+// This function will return kDiceResultInvalidInput if 'input_values' specifies
+// any variable length descriptors. In particular:
+//   * code_descriptor_size must be zero
+//   * authority_descriptor_size must be zero
+//   * config_type must be kDiceConfigTypeInline
+
 #include <stdint.h>
 #include <string.h>
 
 #include "dice/dice.h"
+#include "dice/ops.h"
 #include "dice/utils.h"
 
 // A well-formed certificate, but with zeros in all fields to be filled.
@@ -131,8 +148,8 @@
   memcpy(&buffer[kFieldTable[index].offset], src, kFieldTable[index].length);
 }
 
-DiceResult DiceGenerateCborCertificateFromTemplateOp(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
@@ -163,15 +180,15 @@
   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);
+  result = DiceKeypairFromSeed(context, 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,
+  result = DiceDeriveCdiCertificateId(context, subject_public_key,
                                       subject_public_key_size, subject_id);
   if (result != kDiceResultOk) {
     goto out;
@@ -183,16 +200,16 @@
   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);
+  result =
+      DiceKeypairFromSeed(context, 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,
+  result = DiceDeriveCdiCertificateId(context, authority_public_key,
                                       authority_public_key_size, authority_id);
   if (result != kDiceResultOk) {
     goto out;
@@ -220,22 +237,21 @@
          kFieldTable[kFieldIndexPayload].length);
 
   uint8_t signature[64];
-  result = ops->sign(ops, tbs, kTbsSize, authority_private_key,
-                     authority_private_key_size, sizeof(signature), signature);
+  result = DiceSign(context, tbs, kTbsSize, authority_private_key,
+                    authority_private_key_size, sizeof(signature), signature);
   if (result != kDiceResultOk) {
     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;
-    }
+  result = DiceVerify(context, 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_private_key), subject_private_key);
-  ops->clear_memory(ops, sizeof(authority_private_key), authority_private_key);
+  DiceClearMemory(context, sizeof(subject_private_key), subject_private_key);
+  DiceClearMemory(context, sizeof(authority_private_key),
+                  authority_private_key);
   return result;
 }
diff --git a/src/template_cbor_cert_op_fuzzer.cc b/src/template_cbor_cert_op_fuzzer.cc
deleted file mode 100644
index c21858b..0000000
--- a/src/template_cbor_cert_op_fuzzer.cc
+++ /dev/null
@@ -1,38 +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 "dice/boringssl_ops.h"
-#include "dice/dice.h"
-#include "dice/fuzz_utils.h"
-#include "dice/template_cbor_cert_op.h"
-#include "dice/utils.h"
-
-namespace {
-
-constexpr DiceOps kOps = {
-    .context = NULL,
-    .hash = DiceBsslHashOp,
-    .kdf = DiceBsslKdfOp,
-    .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
-    .sign = DiceBsslEd25519Sign,
-    .verify = DiceBsslEd25519Verify,
-    .generate_certificate = DiceGenerateCborCertificateFromTemplateOp,
-    .clear_memory = DiceClearMemory};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  dice::fuzz::FuzzDiceMainFlow(&kOps, data, size);
-  return 0;
-}
diff --git a/src/template_cbor_cert_op_test.cc b/src/template_cbor_cert_op_test.cc
index d783950..c2d0700 100644
--- a/src/template_cbor_cert_op_test.cc
+++ b/src/template_cbor_cert_op_test.cc
@@ -12,15 +12,12 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/template_cbor_cert_op.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include <memory>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
 #include "dice/known_test_values.h"
 #include "dice/test_framework.h"
@@ -35,22 +32,12 @@
 using dice::test::DiceStateForTest;
 using dice::test::KeyType_Ed25519;
 
-constexpr DiceOps kOps = {
-    .context = NULL,
-    .hash = DiceBsslHashOp,
-    .kdf = DiceBsslKdfOp,
-    .keypair_from_seed = DiceBsslEd25519KeypairFromSeed,
-    .sign = DiceBsslEd25519Sign,
-    .verify = DiceBsslEd25519Verify,
-    .generate_certificate = DiceGenerateCborCertificateFromTemplateOp,
-    .clear_memory = DiceClearMemory};
-
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -79,7 +66,7 @@
                        input_values.config_value);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -104,7 +91,7 @@
   input_values.code_descriptor = descriptor;
   input_values.code_descriptor_size = sizeof(descriptor);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -119,7 +106,7 @@
   input_values.config_descriptor_size = sizeof(descriptor);
   input_values.config_type = kDiceConfigTypeDescriptor;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -133,7 +120,7 @@
   input_values.authority_descriptor = descriptor;
   input_values.authority_descriptor_size = sizeof(descriptor);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -146,7 +133,7 @@
   DiceInputValues input_values = {};
   input_values.mode = kDiceModeDebug;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -158,7 +145,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values,
       12 /*too small*/, next_state.certificate, &next_state.certificate_size,
       next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -172,7 +159,7 @@
   DiceInputValues input_values = {};
   input_values.config_type = (DiceConfigType)55;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -194,7 +181,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -223,7 +210,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -234,7 +221,7 @@
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      kOps, states[0].cdi_attest, CertificateType_Cbor, KeyType_Ed25519,
+      NULL, states[0].cdi_attest, CertificateType_Cbor, KeyType_Ed25519,
       root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
       CertificateType_Cbor, root_certificate, root_certificate_size, &states[1],
diff --git a/src/template_cert_op.c b/src/template_cert_op.c
index df05578..23929ea 100644
--- a/src/template_cert_op.c
+++ b/src/template_cert_op.c
@@ -12,12 +12,29 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/template_cert_op.h"
+// This is an implementation of the DiceGenerateCertificate that generates an
+// X.509 certificate based on a 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 an ASN.1
+// library. This implementation includes only hashes and inline configuration in
+// the DICE extension. For convenience this uses the lower level curve25519
+// implementation in boringssl but does not use anything else (no ASN.1, X.509,
+// etc). This approach may be especially useful in very low level components
+// where simplicity is paramount.
+//
+// This function will return kDiceResultInvalidInput if 'input_values' specifies
+// any variable length descriptors. In particular:
+//   * code_descriptor_size must be zero
+//   * authority_descriptor_size must be zero
+//   * config_type must be kDiceConfigTypeInline
 
 #include <stdint.h>
 #include <string.h>
 
 #include "dice/dice.h"
+#include "dice/ops.h"
 #include "dice/utils.h"
 #include "openssl/curve25519.h"
 #include "openssl/is_boringssl.h"
@@ -148,8 +165,8 @@
   memcpy(&buffer[kFieldTable[index].offset], src, kFieldTable[index].length);
 }
 
-DiceResult DiceGenerateCertificateFromTemplateOp(
-    const DiceOps* ops,
+DiceResult DiceGenerateCertificate(
+    void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const DiceInputValues* input_values, size_t certificate_buffer_size,
@@ -179,7 +196,8 @@
                             subject_private_key_seed);
 
   uint8_t subject_id[20];
-  result = DiceDeriveCdiCertificateId(ops, subject_public_key, 32, subject_id);
+  result =
+      DiceDeriveCdiCertificateId(context, subject_public_key, 32, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -192,8 +210,8 @@
                             authority_private_key_seed);
 
   uint8_t authority_id[20];
-  result =
-      DiceDeriveCdiCertificateId(ops, authority_public_key, 32, authority_id);
+  result = DiceDeriveCdiCertificateId(context, authority_public_key, 32,
+                                      authority_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -234,9 +252,9 @@
   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);
+  DiceClearMemory(context, sizeof(subject_bssl_private_key),
+                  subject_bssl_private_key);
+  DiceClearMemory(context, sizeof(authority_bssl_private_key),
+                  authority_bssl_private_key);
   return result;
 }
diff --git a/src/template_cert_op_fuzzer.cc b/src/template_cert_op_fuzzer.cc
deleted file mode 100644
index 9c6a6ad..0000000
--- a/src/template_cert_op_fuzzer.cc
+++ /dev/null
@@ -1,35 +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 "dice/boringssl_ops.h"
-#include "dice/dice.h"
-#include "dice/fuzz_utils.h"
-#include "dice/template_cert_op.h"
-#include "dice/utils.h"
-
-namespace {
-
-constexpr DiceOps kOps = {
-    .context = NULL,
-    .hash = DiceBsslHashOp,
-    .kdf = DiceBsslKdfOp,
-    .generate_certificate = DiceGenerateCertificateFromTemplateOp,
-    .clear_memory = DiceClearMemory};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  dice::fuzz::FuzzDiceMainFlow(&kOps, data, size);
-  return 0;
-}
diff --git a/src/template_cert_op_test.cc b/src/template_cert_op_test.cc
index ed3c03e..f522e16 100644
--- a/src/template_cert_op_test.cc
+++ b/src/template_cert_op_test.cc
@@ -12,15 +12,12 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#include "dice/template_cert_op.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include <memory>
 
-#include "dice/boringssl_ops.h"
 #include "dice/dice.h"
 #include "dice/known_test_values.h"
 #include "dice/test_framework.h"
@@ -35,19 +32,12 @@
 using dice::test::DiceStateForTest;
 using dice::test::KeyType_Ed25519;
 
-constexpr DiceOps kOps = {
-    .context = NULL,
-    .hash = DiceBsslHashOp,
-    .kdf = DiceBsslKdfOp,
-    .generate_certificate = DiceGenerateCertificateFromTemplateOp,
-    .clear_memory = DiceClearMemory};
-
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -75,7 +65,7 @@
                        input_values.config_value);
 
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -99,7 +89,7 @@
   input_values.code_descriptor = descriptor;
   input_values.code_descriptor_size = sizeof(descriptor);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -114,7 +104,7 @@
   input_values.config_descriptor_size = sizeof(descriptor);
   input_values.config_type = kDiceConfigTypeDescriptor;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -128,7 +118,7 @@
   input_values.authority_descriptor = descriptor;
   input_values.authority_descriptor_size = sizeof(descriptor);
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -141,7 +131,7 @@
   DiceInputValues input_values = {};
   input_values.mode = kDiceModeDebug;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultOk, result);
@@ -153,7 +143,7 @@
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values,
       12 /* too small */, next_state.certificate, &next_state.certificate_size,
       next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultBufferTooSmall, result);
@@ -167,7 +157,7 @@
   DiceInputValues input_values = {};
   input_values.config_type = (DiceConfigType)55;
   DiceResult result = DiceMainFlow(
-      &kOps, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      NULL, 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);
   EXPECT_EQ(kDiceResultInvalidInput, result);
@@ -189,7 +179,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -216,7 +206,7 @@
     inputs[i].mode = kDiceModeNormal;
     EXPECT_EQ(
         kDiceResultOk,
-        DiceMainFlow(&kOps, states[i].cdi_attest, states[i].cdi_seal,
+        DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal,
                      &inputs[i], sizeof(states[i + 1].certificate),
                      states[i + 1].certificate, &states[i + 1].certificate_size,
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
@@ -225,7 +215,7 @@
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      kOps, states[0].cdi_attest, CertificateType_X509, KeyType_Ed25519,
+      NULL, states[0].cdi_attest, CertificateType_X509, KeyType_Ed25519,
       root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
       CertificateType_X509, root_certificate, root_certificate_size, &states[1],
diff --git a/src/test_utils.cc b/src/test_utils.cc
index 07e5f19..48949d1 100644
--- a/src/test_utils.cc
+++ b/src/test_utils.cc
@@ -615,12 +615,12 @@
   }
 }
 
-void CreateFakeUdsCertificate(const DiceOps& ops, const uint8_t uds[32],
+void CreateFakeUdsCertificate(void* context, const uint8_t uds[32],
                               CertificateType cert_type, KeyType key_type,
                               uint8_t certificate[kTestCertSize],
                               size_t* certificate_size) {
   uint8_t raw_key[DICE_PRIVATE_KEY_SEED_SIZE];
-  DiceDeriveCdiPrivateKeySeed(&ops, uds, raw_key);
+  DiceDeriveCdiPrivateKeySeed(context, uds, raw_key);
 
   uint8_t raw_public_key[33];
   size_t raw_public_key_size = 0;
@@ -628,7 +628,7 @@
       KeyFromRawKey(raw_key, key_type, raw_public_key, &raw_public_key_size));
 
   uint8_t id[20];
-  DiceDeriveCdiCertificateId(&ops, raw_public_key, raw_public_key_size, id);
+  DiceDeriveCdiCertificateId(context, raw_public_key, raw_public_key_size, id);
 
   if (cert_type == CertificateType_X509) {
     CreateX509UdsCertificate(key.get(), id, certificate, certificate_size);
diff --git a/src/utils.c b/src/utils.c
index ead530c..4ba1bce 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -29,11 +29,3 @@
     }
   }
 }
-
-void DiceClearMemory(const DiceOps* ops, size_t size, void* address) {
-  (void)ops;
-  volatile uint8_t* p = address;
-  for (size_t i = 0; i < size; i++) {
-    p[i] = 0;
-  }
-}