blob: 2c7d8a6ffece66c5bc3a78b4b3df2efaabcdb3cf [file] [log] [blame]
// 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
// 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>
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 DiceDeriveCdiPrivateKey(
const DiceOps* ops, const uint8_t cdi_attest[DICE_CDI_SIZE],
uint8_t cdi_private_key[DICE_PRIVATE_KEY_SIZE]) {
// Use the CDI as input key material, with fixed salt and info.
return ops->kdf(ops, /*length=*/DICE_PRIVATE_KEY_SIZE, cdi_attest,
/*ikm_size=*/DICE_CDI_SIZE, kAsymSalt, kAsymSaltSize,
/*info=*/(const uint8_t*)"Key Pair", /*info_size=*/8,
DiceResult DiceDeriveCdiCertificateId(const DiceOps* ops,
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,
/*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(const DiceOps* ops,
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 kCodeSize = DICE_HASH_SIZE;
const size_t kConfigSize = DICE_INLINE_CONFIG_SIZE;
const size_t kAuthoritySize = DICE_HASH_SIZE;
const size_t kModeSize = 1;
const size_t kHiddenSize = DICE_HIDDEN_SIZE;
const size_t kCodeOffset = 0;
const size_t kConfigOffset = kCodeOffset + kCodeSize;
const size_t kAuthorityOffset = kConfigOffset + kConfigSize;
const size_t kModeOffset = kAuthorityOffset + kAuthoritySize;
const size_t kHiddenOffset = kModeOffset + kModeSize;
DiceResult result = kDiceResultOk;
// Declare buffers that get cleaned up on 'goto out'.
uint8_t input_buffer[kCodeSize + kConfigSize + kAuthoritySize + kModeSize +
uint8_t attest_input_hash[DICE_HASH_SIZE];
uint8_t seal_input_hash[DICE_HASH_SIZE];
uint8_t current_cdi_private_key[DICE_PRIVATE_KEY_SIZE];
uint8_t next_cdi_private_key[DICE_PRIVATE_KEY_SIZE];
// Assemble the input buffer.
memcpy(&input_buffer[kCodeOffset], input_values->code_hash, kCodeSize);
if (input_values->config_type == kDiceConfigTypeInline) {
memcpy(&input_buffer[kConfigOffset], input_values->config_value,
} else if (!input_values->config_descriptor) {
result = kDiceResultInvalidInput;
goto out;
} else {
result = ops->hash(ops, input_values->config_descriptor,
if (result != kDiceResultOk) {
goto out;
memcpy(&input_buffer[kAuthorityOffset], input_values->authority_hash,
input_buffer[kModeOffset] = input_values->mode;
memcpy(&input_buffer[kHiddenOffset], input_values->hidden, kHiddenSize);
// 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 =
ops->hash(ops, 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);
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,
/*info=*/(const uint8_t*)"CDI_Attest", /*info_size=*/10,
if (result != kDiceResultOk) {
goto out;
result = ops->kdf(
ops, /*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;
// Derive asymmetric private keys from the attestation CDI values.
result =
DiceDeriveCdiPrivateKey(ops, current_cdi_attest, current_cdi_private_key);
if (result != kDiceResultOk) {
goto out;
result = DiceDeriveCdiPrivateKey(ops, next_cdi_attest, next_cdi_private_key);
if (result != kDiceResultOk) {
goto out;
// Generate a certificate for |next_cdi_private_key| with
// |current_cdi_private_key| as the authority.
result = ops->generate_certificate(
ops, next_cdi_private_key, current_cdi_private_key, input_values,
next_cdi_certificate_buffer_size, next_cdi_certificate,
if (result != kDiceResultOk) {
goto 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),
ops->clear_memory(ops, sizeof(next_cdi_private_key), next_cdi_private_key);
return result;