| // 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; |
| } |