diff options
author | Ilias Apalodimas <apalos@gmail.com> | 2024-05-17 18:56:35 +0300 |
---|---|---|
committer | Ilias Apalodimas <apalos@gmail.com> | 2024-05-17 18:56:35 +0300 |
commit | 8598a489b58279d3db55e636f5a516270264f95c (patch) | |
tree | 3c83e98790c42d0328d51723e762e900289c68cd |
Signed-off-by: Ilias Apalodimas <apalos@gmail.com>
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | README | 24 | ||||
-rw-r--r-- | include/dice/config.h | 24 | ||||
-rw-r--r-- | include/dice/dice.h | 166 | ||||
-rw-r--r-- | include/dice/ops.h | 89 | ||||
-rw-r--r-- | include/dice/utils.h | 38 | ||||
m--------- | mbedtls | 0 | ||||
-rw-r--r-- | src/clear_memory.c | 28 | ||||
-rw-r--r-- | src/dice.c | 192 | ||||
-rw-r--r-- | src/main.c | 23 | ||||
-rw-r--r-- | src/mbedtls_ops.c | 512 | ||||
-rw-r--r-- | src/utils.c | 31 |
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 @@ -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]; + } + } +} |