aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlias Apalodimas <apalos@gmail.com>2024-05-17 18:56:35 +0300
committerIlias Apalodimas <apalos@gmail.com>2024-05-17 18:56:35 +0300
commit8598a489b58279d3db55e636f5a516270264f95c (patch)
tree3c83e98790c42d0328d51723e762e900289c68cd
Import opendice & Makefile for DICE prototypeHEADmaster
Signed-off-by: Ilias Apalodimas <apalos@gmail.com>
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--Makefile21
-rw-r--r--README24
-rw-r--r--include/dice/config.h24
-rw-r--r--include/dice/dice.h166
-rw-r--r--include/dice/ops.h89
-rw-r--r--include/dice/utils.h38
m---------mbedtls0
-rw-r--r--src/clear_memory.c28
-rw-r--r--src/dice.c192
-rw-r--r--src/main.c23
-rw-r--r--src/mbedtls_ops.c512
-rw-r--r--src/utils.c31
14 files changed, 1155 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f779739
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+tags
+dice_app
+*.o
+*.pem
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..27944ef
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "mbedtls"]
+ path = mbedtls
+ url = https://github.com/Mbed-TLS/mbedtls.git
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3f20452
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+INCLUDE_DIR = -Iinclude -Imbedtls/include
+CFLAGS = $(INCLUDE_DIR) #-Wall
+LDFLAGS = -Lmbedtls/library/ -lmbedtls -lmbedx509 -lmbedcrypto
+
+src = $(wildcard src/*.c)
+obj = $(src:.c=.o)
+dep = $(obj:.o=.d)
+
+all: dice_app
+
+.PHONY = dice_app
+dice_app: $(obj)
+ make -C mbedtls -j$(nproc) && \
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+ rm -f $(obj) $(dep) dice_app && \
+ make -C mbedtls clean
diff --git a/README b/README
new file mode 100644
index 0000000..7a20e2c
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+# Update
+
+Based on Goole opendice
+https://github.com/google/open-dice
+
+Copy the files below to update
+
+## Copy to src/
+clear_memory.c
+dice.c
+mbedtls_ops.c
+utils.c
+
+## Copy to include/dice/
+config.h
+dice.h
+ops.h
+utils.h
+
+# Compile
+
+git submodule update --init
+cd mbedtls && git submodule update --init
+make
diff --git a/include/dice/config.h b/include/dice/config.h
new file mode 100644
index 0000000..107e4d5
--- /dev/null
+++ b/include/dice/config.h
@@ -0,0 +1,24 @@
+// 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_CONFIG_MBEDTLS_ECDSA_P256_DICE_CONFIG_H_
+#define DICE_CONFIG_MBEDTLS_ECDSA_P256_DICE_CONFIG_H_
+
+// ECDSA-P256
+#define DICE_PUBLIC_KEY_SIZE 33
+#define DICE_PRIVATE_KEY_SIZE 32
+#define DICE_SIGNATURE_SIZE 64
+#define DICE_PROFILE_NAME "openssl.example.p256"
+
+#endif // DICE_CONFIG_MBEDTLS_ECDSA_P256_DICE_DICE_CONFIG_H_
diff --git a/include/dice/dice.h b/include/dice/dice.h
new file mode 100644
index 0000000..cf54942
--- /dev/null
+++ b/include/dice/dice.h
@@ -0,0 +1,166 @@
+// 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_DICE_H_
+#define DICE_DICE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DICE_CDI_SIZE 32
+#define DICE_HASH_SIZE 64
+#define DICE_HIDDEN_SIZE 64
+#define DICE_INLINE_CONFIG_SIZE 64
+#define DICE_PRIVATE_KEY_SEED_SIZE 32
+#define DICE_ID_SIZE 20
+
+typedef enum {
+ kDiceResultOk,
+ kDiceResultInvalidInput,
+ kDiceResultBufferTooSmall,
+ kDiceResultPlatformError,
+} DiceResult;
+
+typedef enum {
+ kDiceModeNotInitialized,
+ kDiceModeNormal,
+ kDiceModeDebug,
+ kDiceModeMaintenance,
+} DiceMode;
+
+typedef enum {
+ kDiceConfigTypeInline,
+ kDiceConfigTypeDescriptor,
+} DiceConfigType;
+
+// Contains a full set of input values describing the target program or system.
+// See the Open Profile for DICE specification for a detailed explanation of
+// these inputs.
+//
+// Fields:
+// code_hash: A hash or similar representation of the target code.
+// code_descriptor: An optional descriptor to be included in the certificate.
+// This descriptor is opaque to the DICE flow and is included verbatim
+// in the certificate with no validation. May be null.
+// code_descriptor_size: The size in bytes of |code_descriptor|.
+// config_type: Indicates how to interpret the remaining config-related
+// fields. If the type is 'inline', then the 64 byte configuration input
+// value must be provided in |config_value| and |config_descriptor| is
+// ignored. If the type is 'descriptor', then |config_descriptor| is
+// hashed to get the configuration input value and |config_value| is
+// ignored.
+// config_value: A 64-byte configuration input value when |config_type| is
+// kDiceConfigTypeInline. Otherwise, this field is ignored.
+// config_descriptor: A descriptor to be hashed for the configuration input
+// value when |config_type| is kDiceConfigTypeDescriptor. Otherwise,
+// this field is ignored and may be null.
+// config_descriptor_size: The size in bytes of |config_descriptor|.
+// authority_hash: A hash or similar representation of the authority used to
+// verify the target code. If the code is not verified or the authority
+// is implicit, for example hard coded as part of the code currently
+// executing, then this value should be set to all zero bytes.
+// authority_descriptor: An optional descriptor to be included in the
+// certificate. This descriptor is opaque to the DICE flow and is
+// included verbatim in the certificate with no validation. May be null.
+// authority_descriptor_size: The size in bytes of |authority_descriptor|.
+// mode: The current operating mode.
+// hidden: Additional input which will not appear in certificates. If this is
+// not used it should be set to all zero bytes.
+typedef struct DiceInputValues_ {
+ uint8_t code_hash[DICE_HASH_SIZE];
+ const uint8_t* code_descriptor;
+ size_t code_descriptor_size;
+ DiceConfigType config_type;
+ uint8_t config_value[DICE_INLINE_CONFIG_SIZE];
+ const uint8_t* config_descriptor;
+ size_t config_descriptor_size;
+ uint8_t authority_hash[DICE_HASH_SIZE];
+ const uint8_t* authority_descriptor;
+ size_t authority_descriptor_size;
+ DiceMode mode;
+ uint8_t hidden[DICE_HIDDEN_SIZE];
+} DiceInputValues;
+
+// Derives a |cdi_private_key_seed| from a |cdi_attest| value. On success
+// populates |cdi_private_key_seed| and returns kDiceResultOk.
+DiceResult DiceDeriveCdiPrivateKeySeed(
+ 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.
+DiceResult DiceDeriveCdiCertificateId(void* context,
+ const uint8_t* cdi_public_key,
+ size_t cdi_public_key_size,
+ uint8_t id[DICE_ID_SIZE]);
+
+// Executes the main DICE flow.
+//
+// Given a full set of input values and the current CDI values, computes the
+// next CDI values and a matching certificate. See the Open Profile for DICE
+// specification for a detailed explanation of this flow.
+// In certain cases, the caller may not need to generate the CDI certificate.
+// The caller should signal this by setting the certificate parameters to
+// null/zero values appropriately.
+//
+// Parameters:
+// context: Context provided by the caller that is opaque to this library
+// 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
+// arguments.
+// input_values: A set of input values describing the target program or
+// system.
+// next_cdi_certificate_buffer_size: The size in bytes of the buffer pointed
+// to by the |next_cdi_certificate| argument. This should be set to zero
+// if next CDI certificate should not be computed.
+// next_cdi_certificate: On success, will be populated with the generated
+// certificate, up to |next_cdi_certificate_buffer_size| in size. If the
+// certificate cannot fit in the buffer, |next_cdi_certificate_size| is
+// populated with the required size and kDiceResultBufferTooSmall is
+// returned. This should be set to NULL if next CDI certificate should
+// not be computed.
+// next_cdi_certificate_actual_size: On success, will be populated with the
+// size, in bytes, of the certificate data written to
+// |next_cdi_certificate|. If kDiceResultBufferTooSmall is returned, will
+// be populated with the required buffer size. This should be set to NULL
+// if next CDI certificate should not be computed.
+// next_cdi_attest: On success, will be populated with the next CDI value for
+// attestation.
+// next_cdi_seal: On success, will be populated with the next CDI value for
+// sealing.
+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,
+ size_t next_cdi_certificate_buffer_size,
+ uint8_t* next_cdi_certificate,
+ size_t* next_cdi_certificate_actual_size,
+ uint8_t next_cdi_attest[DICE_CDI_SIZE],
+ uint8_t next_cdi_seal[DICE_CDI_SIZE]);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DICE_DICE_H_
diff --git a/include/dice/ops.h b/include/dice/ops.h
new file mode 100644
index 0000000..53f8d8e
--- /dev/null
+++ b/include/dice/ops.h
@@ -0,0 +1,89 @@
+// 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/config.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_SIZE],
+ uint8_t private_key[DICE_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
+// the buffer where the calculated signature is written.
+DiceResult DiceSign(void* context, const uint8_t* message, size_t message_size,
+ const uint8_t private_key[DICE_PRIVATE_KEY_SIZE],
+ uint8_t signature[DICE_SIGNATURE_SIZE]);
+
+// 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[DICE_SIGNATURE_SIZE],
+ const uint8_t public_key[DICE_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/utils.h b/include/dice/utils.h
new file mode 100644
index 0000000..10e9c4b
--- /dev/null
+++ b/include/dice/utils.h
@@ -0,0 +1,38 @@
+// 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_UTILS_H_
+#define DICE_UTILS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "dice/dice.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Converts arbitrary bytes to ascii hex, no NUL terminator is added. Up to
+// |num_bytes| from |in| will be converted, and up to |out_size| bytes will be
+// written to |out|. If |out_size| is less than |num_bytes| * 2, the output will
+// be truncated at |out_size|.
+void DiceHexEncode(const uint8_t* in, size_t num_bytes, void* out,
+ size_t out_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DICE_UTILS_H_
diff --git a/mbedtls b/mbedtls
new file mode 160000
+Subproject bdce65700ecb11adbc845993199badfb1c64072
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
new file mode 100644
index 0000000..4bbd712
--- /dev/null
+++ b/src/dice.c
@@ -0,0 +1,192 @@
+// 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 <string.h>
+
+#include "dice/ops.h"
+
+#define DICE_CODE_SIZE DICE_HASH_SIZE
+#define DICE_CONFIG_SIZE DICE_INLINE_CONFIG_SIZE
+#define DICE_AUTHORITY_SIZE DICE_HASH_SIZE
+#define DICE_MODE_SIZE 1
+
+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,
+ 0xB3, 0xA7, 0x71, 0x24, 0x03, 0x5C, 0x03, 0xF8, 0xE1, 0xBE, 0x60,
+ 0x35, 0xD3, 0x1F, 0x28, 0x28, 0x21, 0xA7, 0x45, 0x0A, 0x02, 0x22,
+ 0x2A, 0xB1, 0xB3, 0xCF, 0xF1, 0x67, 0x9B, 0x05, 0xAB, 0x1C, 0xA5,
+ 0xD1, 0xAF, 0xFB, 0x78, 0x9C, 0xCD, 0x2B, 0x0B, 0x3B};
+static const size_t kAsymSaltSize = 64;
+
+static const uint8_t kIdSalt[] = {
+ 0xDB, 0xDB, 0xAE, 0xBC, 0x80, 0x20, 0xDA, 0x9F, 0xF0, 0xDD, 0x5A,
+ 0x24, 0xC8, 0x3A, 0xA5, 0xA5, 0x42, 0x86, 0xDF, 0xC2, 0x63, 0x03,
+ 0x1E, 0x32, 0x9B, 0x4D, 0xA1, 0x48, 0x43, 0x06, 0x59, 0xFE, 0x62,
+ 0xCD, 0xB5, 0xB7, 0xE1, 0xE0, 0x0F, 0xC6, 0x80, 0x30, 0x67, 0x11,
+ 0xEB, 0x44, 0x4A, 0xF7, 0x72, 0x09, 0x35, 0x94, 0x96, 0xFC, 0xFF,
+ 0x1D, 0xB9, 0x52, 0x0B, 0xA5, 0x1C, 0x7B, 0x29, 0xEA};
+static const size_t kIdSaltSize = 64;
+
+DiceResult DiceDeriveCdiPrivateKeySeed(
+ 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 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(void* context,
+ const uint8_t* cdi_public_key,
+ size_t cdi_public_key_size,
+ uint8_t id[DICE_ID_SIZE]) {
+ // Use the public key as input key material, with fixed salt and info.
+ DiceResult result =
+ 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;
+ }
+ return result;
+}
+
+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,
+ size_t next_cdi_certificate_buffer_size,
+ uint8_t* next_cdi_certificate,
+ size_t* next_cdi_certificate_actual_size,
+ uint8_t next_cdi_attest[DICE_CDI_SIZE],
+ uint8_t next_cdi_seal[DICE_CDI_SIZE]) {
+ // This implementation serializes the inputs for a one-shot hash. On some
+ // platforms, using a multi-part hash operation may be more optimal. The
+ // combined input buffer has this layout:
+ // ---------------------------------------------------------------------------
+ // | Code Input | Config Input | Authority Input | Mode Input | Hidden Input |
+ // ---------------------------------------------------------------------------
+ const size_t kCodeOffset = 0;
+ const size_t kConfigOffset = kCodeOffset + DICE_CODE_SIZE;
+ const size_t kAuthorityOffset = kConfigOffset + DICE_CONFIG_SIZE;
+ const size_t kModeOffset = kAuthorityOffset + DICE_AUTHORITY_SIZE;
+ const size_t kHiddenOffset = kModeOffset + DICE_MODE_SIZE;
+
+ DiceResult result = kDiceResultOk;
+
+ // Declare buffers that get cleaned up on 'goto out'.
+ uint8_t input_buffer[DICE_CODE_SIZE + DICE_CONFIG_SIZE + DICE_AUTHORITY_SIZE +
+ DICE_MODE_SIZE + DICE_HIDDEN_SIZE];
+ uint8_t attest_input_hash[DICE_HASH_SIZE];
+ uint8_t seal_input_hash[DICE_HASH_SIZE];
+ uint8_t current_cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE];
+ uint8_t next_cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE];
+
+ // Assemble the input buffer.
+ memcpy(&input_buffer[kCodeOffset], input_values->code_hash, DICE_CODE_SIZE);
+ if (input_values->config_type == kDiceConfigTypeInline) {
+ memcpy(&input_buffer[kConfigOffset], input_values->config_value,
+ DICE_CONFIG_SIZE);
+ } else if (!input_values->config_descriptor) {
+ result = kDiceResultInvalidInput;
+ goto out;
+ } else {
+ result = DiceHash(context, input_values->config_descriptor,
+ input_values->config_descriptor_size,
+ &input_buffer[kConfigOffset]);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+ }
+ memcpy(&input_buffer[kAuthorityOffset], input_values->authority_hash,
+ DICE_AUTHORITY_SIZE);
+ input_buffer[kModeOffset] = input_values->mode;
+ memcpy(&input_buffer[kHiddenOffset], input_values->hidden, DICE_HIDDEN_SIZE);
+
+ // Hash the appropriate input values for both attestation and sealing. For
+ // attestation all the inputs are used, and for sealing only the authority,
+ // mode, and hidden inputs are used.
+ result =
+ DiceHash(context, input_buffer, sizeof(input_buffer), attest_input_hash);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+ result = DiceHash(context, &input_buffer[kAuthorityOffset],
+ DICE_AUTHORITY_SIZE + DICE_MODE_SIZE + DICE_HIDDEN_SIZE,
+ 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 = 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 = 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) {
+ goto out;
+ }
+
+ // Create the CDI certificate only if it is required (i.e. non-null/non-zero
+ // values are provided for the next CDI certificate parameters).
+ if (next_cdi_certificate == NULL &&
+ next_cdi_certificate_actual_size == NULL &&
+ next_cdi_certificate_buffer_size == 0) {
+ goto out;
+ }
+
+ // Derive asymmetric private key seeds from the attestation CDI values.
+ result = DiceDeriveCdiPrivateKeySeed(context, current_cdi_attest,
+ current_cdi_private_key_seed);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+ result = DiceDeriveCdiPrivateKeySeed(context, next_cdi_attest,
+ next_cdi_private_key_seed);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ // Generate a certificate for |next_cdi_private_key_seed| with
+ // |current_cdi_private_key_seed| as the authority.
+ 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);
+
+out:
+ // Clear sensitive memory.
+ 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/main.c b/src/main.c
new file mode 100644
index 0000000..3f6ea49
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <dice/dice.h>
+
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ uint8_t cdi_buffer[DICE_CDI_SIZE] = {0};
+ uint8_t cert_buffer[2048];
+ DiceResult ret;
+ size_t cert_size;
+ DiceInputValues input_values = {0};
+ int i;
+
+ ret = DiceMainFlow(/*context=*/NULL, cdi_buffer, cdi_buffer,
+ &input_values, sizeof(cert_buffer), cert_buffer,
+ &cert_size, cdi_buffer, cdi_buffer);
+ for (i = 0; i < DICE_CDI_SIZE; i++) {
+ printf("%c", cdi_buffer[i]);
+ }
+
+ return (int)(ret);
+}
diff --git a/src/mbedtls_ops.c b/src/mbedtls_ops.c
new file mode 100644
index 0000000..15318f1
--- /dev/null
+++ b/src/mbedtls_ops.c
@@ -0,0 +1,512 @@
+// 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.
+
+// 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"
+#include "mbedtls/bignum.h"
+#include "mbedtls/ecdsa.h"
+#include "mbedtls/ecp.h"
+#include "mbedtls/hkdf.h"
+#include "mbedtls/hmac_drbg.h"
+#include "mbedtls/md.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/pk.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/x509_crt.h"
+
+#define DICE_MAX_CERTIFICATE_SIZE 2048
+#define DICE_MAX_EXTENSION_SIZE 2048
+#define DICE_MAX_KEY_ID_SIZE 40
+
+static DiceResult SetupKeyPair(
+ const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
+ mbedtls_pk_context* context) {
+ if (0 !=
+ mbedtls_pk_setup(context, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY))) {
+ return kDiceResultPlatformError;
+ }
+ // Use the |private_key_seed| directly to seed a PRNG which is then in turn
+ // used to generate the private key. This implementation uses HMAC_DRBG in a
+ // loop with no reduction, like RFC6979.
+ DiceResult result = kDiceResultOk;
+ mbedtls_hmac_drbg_context rng_context;
+ mbedtls_hmac_drbg_init(&rng_context);
+ if (0 != mbedtls_hmac_drbg_seed_buf(
+ &rng_context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
+ private_key_seed, DICE_PRIVATE_KEY_SEED_SIZE)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1,
+ mbedtls_pk_ec(*context),
+ mbedtls_hmac_drbg_random, &rng_context)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+
+out:
+ mbedtls_hmac_drbg_free(&rng_context);
+ return result;
+}
+
+static DiceResult GetIdFromKey(void* context,
+ const mbedtls_pk_context* pk_context,
+ uint8_t id[DICE_ID_SIZE]) {
+ uint8_t raw_public_key[33];
+ size_t raw_public_key_size = 0;
+ mbedtls_ecp_keypair* key = mbedtls_pk_ec(*pk_context);
+
+ if (0 != mbedtls_ecp_point_write_binary(
+ &key->MBEDTLS_PRIVATE(grp), &key->MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_COMPRESSED,
+ &raw_public_key_size, raw_public_key, sizeof(raw_public_key))) {
+ return kDiceResultPlatformError;
+ }
+ return DiceDeriveCdiCertificateId(context, raw_public_key,
+ raw_public_key_size, id);
+}
+
+// 54 byte name is prefix (13), hex id (40), and a null terminator.
+static void GetNameFromId(const uint8_t id[DICE_ID_SIZE], char name[54]) {
+ strcpy(name, "serialNumber=");
+ DiceHexEncode(id, /*num_bytes=*/DICE_ID_SIZE, (uint8_t*)&name[13],
+ /*out_size=*/40);
+ name[53] = '\0';
+}
+
+static DiceResult GetSubjectKeyIdFromId(const uint8_t id[DICE_ID_SIZE],
+ size_t buffer_size, uint8_t* buffer,
+ size_t* actual_size) {
+ uint8_t* pos = buffer + buffer_size;
+ int length_or_error =
+ mbedtls_asn1_write_octet_string(&pos, buffer, id, DICE_ID_SIZE);
+ if (length_or_error < 0) {
+ return kDiceResultPlatformError;
+ }
+ *actual_size = length_or_error;
+ memmove(buffer, pos, *actual_size);
+ return kDiceResultOk;
+}
+
+static int AddAuthorityKeyIdEncoding(uint8_t** pos, uint8_t* start,
+ int length) {
+ // From RFC 5280 4.2.1.1.
+ const int kKeyIdentifierTag = 0;
+
+ int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD.
+ MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length));
+ MBEDTLS_ASN1_CHK_ADD(
+ length,
+ mbedtls_asn1_write_tag(
+ pos, start, MBEDTLS_ASN1_CONTEXT_SPECIFIC | kKeyIdentifierTag));
+
+ MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length));
+ MBEDTLS_ASN1_CHK_ADD(
+ length,
+ mbedtls_asn1_write_tag(pos, start,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
+ return length;
+}
+
+static DiceResult GetAuthorityKeyIdFromId(const uint8_t id[DICE_ID_SIZE],
+ size_t buffer_size, uint8_t* buffer,
+ size_t* actual_size) {
+ uint8_t* pos = buffer + buffer_size;
+ int length_or_error =
+ mbedtls_asn1_write_raw_buffer(&pos, buffer, id, DICE_ID_SIZE);
+ if (length_or_error < 0) {
+ return kDiceResultPlatformError;
+ }
+ length_or_error = AddAuthorityKeyIdEncoding(&pos, buffer, length_or_error);
+ if (length_or_error < 0) {
+ return kDiceResultPlatformError;
+ }
+ *actual_size = length_or_error;
+ memmove(buffer, pos, *actual_size);
+ return kDiceResultOk;
+}
+
+static uint8_t GetFieldTag(uint8_t tag) {
+ return MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | tag;
+}
+
+// Can be used with MBEDTLS_ASN1_CHK_ADD.
+static int WriteExplicitModeField(uint8_t tag, int value, uint8_t** pos,
+ uint8_t* start) {
+ // ASN.1 constants not defined by mbedtls.
+ const uint8_t kEnumTypeTag = 10;
+
+ int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD.
+ int field_length = 0;
+ MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_int(pos, start, value));
+ // Overwrite the 'int' type.
+ ++(*pos);
+ --field_length;
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_tag(pos, start, kEnumTypeTag));
+
+ // Explicitly tagged, so add the field tag too.
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_len(pos, start, field_length));
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag)));
+ return field_length;
+}
+
+// Can be used with MBEDTLS_ASN1_CHK_ADD.
+static int WriteExplicitUtf8StringField(uint8_t tag, const void* value,
+ size_t value_size, uint8_t** pos,
+ uint8_t* start) {
+ int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD.
+ int field_length = 0;
+ MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_utf8_string(
+ pos, start, value, value_size));
+ // Explicitly tagged, so add the field tag too.
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_len(pos, start, field_length));
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag)));
+ return field_length;
+}
+
+// Can be used with MBEDTLS_ASN1_CHK_ADD.
+static int WriteExplicitOctetStringField(uint8_t tag, const uint8_t* value,
+ size_t value_size, uint8_t** pos,
+ uint8_t* start) {
+ int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD.
+ int field_length = 0;
+ MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_octet_string(
+ pos, start, value, value_size));
+ // Explicitly tagged, so add the field tag too.
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_len(pos, start, field_length));
+ MBEDTLS_ASN1_CHK_ADD(field_length,
+ mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag)));
+ return field_length;
+}
+
+static int GetDiceExtensionDataHelper(const DiceInputValues* input_values,
+ uint8_t** pos, uint8_t* start) {
+ // ASN.1 tags for extension fields.
+ const uint8_t kDiceFieldCodeHash = 0;
+ const uint8_t kDiceFieldCodeDescriptor = 1;
+ const uint8_t kDiceFieldConfigHash = 2;
+ const uint8_t kDiceFieldConfigDescriptor = 3;
+ const uint8_t kDiceFieldAuthorityHash = 4;
+ const uint8_t kDiceFieldAuthorityDescriptor = 5;
+ const uint8_t kDiceFieldMode = 6;
+ const uint8_t kDiceFieldProfileName = 7;
+
+ // Build up the extension ASN.1 in reverse order.
+ int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD.
+ int length = 0;
+
+ // Add the profile name field.
+ if (DICE_PROFILE_NAME) {
+ MBEDTLS_ASN1_CHK_ADD(length, WriteExplicitUtf8StringField(
+ kDiceFieldProfileName, DICE_PROFILE_NAME,
+ strlen(DICE_PROFILE_NAME), pos, start));
+ }
+
+ // Add the mode field.
+ MBEDTLS_ASN1_CHK_ADD(
+ length,
+ WriteExplicitModeField(kDiceFieldMode, input_values->mode, pos, start));
+
+ // Add the authorityDescriptor field, if applicable.
+ if (input_values->authority_descriptor_size > 0) {
+ MBEDTLS_ASN1_CHK_ADD(
+ length,
+ WriteExplicitOctetStringField(
+ kDiceFieldAuthorityDescriptor, input_values->authority_descriptor,
+ input_values->authority_descriptor_size, pos, start));
+ }
+
+ // Add the authorityHash field.
+ MBEDTLS_ASN1_CHK_ADD(
+ length, WriteExplicitOctetStringField(kDiceFieldAuthorityHash,
+ input_values->authority_hash,
+ DICE_HASH_SIZE, pos, start));
+
+ // Add the configurationDescriptor field (and configurationHash field, if
+ // applicable).
+ if (input_values->config_type == kDiceConfigTypeDescriptor) {
+ uint8_t hash[DICE_HASH_SIZE];
+ int result = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
+ input_values->config_descriptor,
+ input_values->config_descriptor_size, hash);
+ if (result) {
+ return result;
+ }
+ MBEDTLS_ASN1_CHK_ADD(
+ length, WriteExplicitOctetStringField(
+ kDiceFieldConfigDescriptor, input_values->config_descriptor,
+ input_values->config_descriptor_size, pos, start));
+ MBEDTLS_ASN1_CHK_ADD(
+ length, WriteExplicitOctetStringField(kDiceFieldConfigHash, hash,
+ DICE_HASH_SIZE, pos, start));
+ } else if (input_values->config_type == kDiceConfigTypeInline) {
+ MBEDTLS_ASN1_CHK_ADD(
+ length, WriteExplicitOctetStringField(
+ kDiceFieldConfigDescriptor, input_values->config_value,
+ DICE_INLINE_CONFIG_SIZE, pos, start));
+ }
+
+ // Add the code descriptor field, if applicable.
+ if (input_values->code_descriptor_size > 0) {
+ MBEDTLS_ASN1_CHK_ADD(
+ length, WriteExplicitOctetStringField(
+ kDiceFieldCodeDescriptor, input_values->code_descriptor,
+ input_values->code_descriptor_size, pos, start));
+ }
+
+ // Add the code hash field.
+ MBEDTLS_ASN1_CHK_ADD(length, WriteExplicitOctetStringField(
+ kDiceFieldCodeHash, input_values->code_hash,
+ DICE_HASH_SIZE, pos, start));
+
+ // Add the sequence length and tag.
+ MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length));
+ MBEDTLS_ASN1_CHK_ADD(
+ length,
+ mbedtls_asn1_write_tag(pos, start,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
+ return length;
+}
+
+static DiceResult GetDiceExtensionData(const DiceInputValues* input_values,
+ size_t buffer_size, uint8_t* buffer,
+ size_t* actual_size) {
+ uint8_t* pos = buffer + buffer_size;
+ int length_or_error = GetDiceExtensionDataHelper(input_values, &pos, buffer);
+ if (length_or_error == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL) {
+ return kDiceResultBufferTooSmall;
+ } else if (length_or_error < 0) {
+ return kDiceResultPlatformError;
+ }
+ *actual_size = length_or_error;
+ memmove(buffer, pos, *actual_size);
+ return kDiceResultOk;
+}
+
+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;
+ }
+ return kDiceResultOk;
+}
+
+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)) {
+ return kDiceResultPlatformError;
+ }
+ return kDiceResultOk;
+}
+
+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) {
+ // 1.3.6.1.4.1.11129.2.1.24
+ // iso.org.dod.internet.private.enterprise.
+ // google.googleSecurity.certificateExtensions.diceAttestationData
+ const char* kDiceExtensionOid =
+ MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_DOD
+ "\x01\x04\x01\xd6\x79\x02\x01\x18";
+ const size_t kDiceExtensionOidLength = 10;
+
+ DiceResult result = kDiceResultOk;
+
+ // Initialize variables cleaned up on 'goto out'.
+ mbedtls_pk_context authority_key_context;
+ mbedtls_pk_init(&authority_key_context);
+ mbedtls_pk_context subject_key_context;
+ mbedtls_pk_init(&subject_key_context);
+ mbedtls_x509write_cert cert_context;
+ mbedtls_x509write_crt_init(&cert_context);
+ mbedtls_mpi serial_number;
+ mbedtls_mpi_init(&serial_number);
+
+ // Derive key pairs and IDs.
+ result = SetupKeyPair(authority_private_key_seed, &authority_key_context);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ uint8_t authority_id[DICE_ID_SIZE];
+ result = GetIdFromKey(context, &authority_key_context, authority_id);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ char authority_name[54];
+ GetNameFromId(authority_id, authority_name);
+
+ uint8_t authority_key_id[DICE_MAX_KEY_ID_SIZE];
+ size_t authority_key_id_size = 0;
+ result = GetAuthorityKeyIdFromId(authority_id, sizeof(authority_key_id),
+ authority_key_id, &authority_key_id_size);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+ result = SetupKeyPair(subject_private_key_seed, &subject_key_context);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ uint8_t subject_id[DICE_ID_SIZE];
+ result = GetIdFromKey(context, &subject_key_context, subject_id);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ char subject_name[54];
+ GetNameFromId(subject_id, subject_name);
+
+ uint8_t subject_key_id[DICE_MAX_KEY_ID_SIZE];
+ size_t subject_key_id_size = 0;
+ result = GetSubjectKeyIdFromId(subject_id, sizeof(subject_key_id),
+ subject_key_id, &subject_key_id_size);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ uint8_t dice_extension[DICE_MAX_EXTENSION_SIZE];
+ size_t dice_extension_size = 0;
+ result = GetDiceExtensionData(input_values, sizeof(dice_extension),
+ dice_extension, &dice_extension_size);
+ if (result != kDiceResultOk) {
+ goto out;
+ }
+
+ // Construct the certificate.
+ mbedtls_x509write_crt_set_version(&cert_context, MBEDTLS_X509_CRT_VERSION_3);
+ if (0 !=
+ mbedtls_mpi_read_binary(&serial_number, subject_id, sizeof(subject_id))) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_x509write_crt_set_serial_raw(&cert_context, subject_id,
+ sizeof(subject_id))) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ // '20180322235959' is the date of publication of the DICE specification. Here
+ // it's used as a somewhat arbitrary backstop. '99991231235959' is suggested
+ // by RFC 5280 in cases where expiry is not meaningful. Basically, the
+ // certificate never expires.
+ if (0 != mbedtls_x509write_crt_set_validity(&cert_context, "20180322235959",
+ "99991231235959")) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 !=
+ mbedtls_x509write_crt_set_issuer_name(&cert_context, authority_name)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 !=
+ mbedtls_x509write_crt_set_subject_name(&cert_context, subject_name)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ mbedtls_x509write_crt_set_subject_key(&cert_context, &subject_key_context);
+ mbedtls_x509write_crt_set_issuer_key(&cert_context, &authority_key_context);
+ mbedtls_x509write_crt_set_md_alg(&cert_context, MBEDTLS_MD_SHA512);
+ if (0 != mbedtls_x509write_crt_set_extension(
+ &cert_context, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER,
+ MBEDTLS_OID_SIZE(MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER),
+ /*critical=*/0, authority_key_id, authority_key_id_size)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_x509write_crt_set_extension(
+ &cert_context, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER,
+ MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER),
+ /*critical=*/0, subject_key_id, subject_key_id_size)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_x509write_crt_set_key_usage(&cert_context,
+ MBEDTLS_X509_KU_KEY_CERT_SIGN)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_x509write_crt_set_basic_constraints(&cert_context,
+ /*is_ca=*/1,
+ /*max_pathlen=*/-1)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ if (0 != mbedtls_x509write_crt_set_extension(
+ &cert_context, kDiceExtensionOid, kDiceExtensionOidLength,
+ /*critical=*/1, dice_extension, dice_extension_size)) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ // This implementation is deterministic and assumes entropy is not available.
+ // If this code is run where entropy is available, however, f_rng and p_rng
+ // should be set to use that entropy. As is, we'll provide a DRBG for blinding
+ // but it will be ineffective.
+ mbedtls_hmac_drbg_context drbg;
+ mbedtls_hmac_drbg_init(&drbg);
+ mbedtls_hmac_drbg_seed_buf(&drbg,
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
+ subject_key_id, subject_key_id_size);
+ uint8_t tmp_buffer[DICE_MAX_CERTIFICATE_SIZE];
+ int length_or_error =
+ mbedtls_x509write_crt_der(&cert_context, tmp_buffer, sizeof(tmp_buffer),
+ mbedtls_hmac_drbg_random, &drbg);
+ mbedtls_hmac_drbg_free(&drbg);
+ if (length_or_error < 0) {
+ result = kDiceResultPlatformError;
+ goto out;
+ }
+ *certificate_actual_size = length_or_error;
+ if (*certificate_actual_size > certificate_buffer_size) {
+ result = kDiceResultBufferTooSmall;
+ goto out;
+ }
+ // The certificate has been written to the end of tmp_buffer. Skip unused
+ // buffer when copying.
+ memcpy(certificate,
+ &tmp_buffer[sizeof(tmp_buffer) - *certificate_actual_size],
+ *certificate_actual_size);
+
+out:
+ mbedtls_mpi_free(&serial_number);
+ mbedtls_x509write_crt_free(&cert_context);
+ mbedtls_pk_free(&authority_key_context);
+ mbedtls_pk_free(&subject_key_context);
+ return result;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..4ba1bce
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,31 @@
+// 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/utils.h"
+
+#include <stdint.h>
+
+void DiceHexEncode(const uint8_t* in, size_t num_bytes, void* out,
+ size_t out_size) {
+ const uint8_t kHexMap[16] = "0123456789abcdef";
+ size_t in_pos = 0;
+ size_t out_pos = 0;
+ uint8_t* out_bytes = out;
+ for (in_pos = 0; in_pos < num_bytes && out_pos < out_size; ++in_pos) {
+ out_bytes[out_pos++] = kHexMap[(in[in_pos] >> 4)];
+ if (out_pos < out_size) {
+ out_bytes[out_pos++] = kHexMap[in[in_pos] & 0xF];
+ }
+ }
+}