| // Copyright 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "dice/dice.h" |
| |
| #include "dice/known_test_values.h" |
| #include "dice/test_framework.h" |
| #include "dice/utils.h" |
| #include "openssl/crypto.h" |
| #include "openssl/digest.h" |
| #include "openssl/hkdf.h" |
| #include "openssl/sha.h" |
| |
| namespace { |
| |
| const size_t kFakeCertSize = 200; |
| |
| struct FakeDiceOps { |
| FakeDiceOps() { CRYPTO_library_init(); } |
| |
| // DiceOps calls to |hash| forward here. |
| DiceResult Hash(const uint8_t* input, size_t input_size, |
| uint8_t output[DICE_HASH_SIZE]) { |
| SHA512(input, input_size, output); |
| hash_count_++; |
| return hash_result_; |
| } |
| |
| // DiceOps calls to |kdf| forward here. |
| DiceResult Kdf(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) { |
| HKDF(output, length, EVP_sha512(), ikm, ikm_size, salt, salt_size, info, |
| info_size); |
| kdf_count_++; |
| return kdf_result_; |
| } |
| |
| // DiceOps calls to |generate_certificate| forward here. |
| DiceResult GenerateCertificate( |
| 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) { |
| const uint8_t kFakeCert[kFakeCertSize] = {}; |
| (void)subject_private_key_seed; |
| (void)authority_private_key_seed; |
| (void)input_values; |
| generate_certificate_count_++; |
| if (certificate_buffer_size < kFakeCertSize) { |
| *certificate_actual_size = kFakeCertSize; |
| return kDiceResultBufferTooSmall; |
| } |
| *certificate_actual_size = kFakeCertSize; |
| memcpy(certificate, kFakeCert, kFakeCertSize); |
| return generate_certificate_result_; |
| } |
| |
| // Set these in a test to induce errors. |
| DiceResult hash_result_ = kDiceResultOk; |
| DiceResult kdf_result_ = kDiceResultOk; |
| DiceResult generate_certificate_result_ = kDiceResultOk; |
| |
| // These will be incremented on every DiceOps call. |
| int hash_count_ = 0; |
| int kdf_count_ = 0; |
| int generate_certificate_count_ = 0; |
| }; |
| |
| extern "C" { |
| // These callbacks forward to a FakeDiceOps instance. |
| DiceResult DiceHash(void* context, const uint8_t* input, size_t input_size, |
| uint8_t output[DICE_HASH_SIZE]) { |
| return reinterpret_cast<FakeDiceOps*>(context)->Hash(input, input_size, |
| output); |
| } |
| |
| DiceResult DiceKdf(void* context, size_t length, const uint8_t* ikm, |
| size_t ikm_size, const uint8_t* salt, size_t salt_size, |
| const uint8_t* info, size_t info_size, uint8_t* output) { |
| return reinterpret_cast<FakeDiceOps*>(context)->Kdf( |
| length, ikm, ikm_size, salt, salt_size, info, info_size, output); |
| } |
| |
| DiceResult DiceGenerateCertificate( |
| void* context, |
| const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], |
| const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], |
| const DiceInputValues* input_values, size_t certificate_buffer_size, |
| uint8_t* certificate, size_t* certificate_actual_size) { |
| return reinterpret_cast<FakeDiceOps*>(context)->GenerateCertificate( |
| subject_private_key_seed, authority_private_key_seed, input_values, |
| certificate_buffer_size, certificate, certificate_actual_size); |
| } |
| } |
| |
| struct DiceStateForTest { |
| uint8_t cdi_attest[DICE_CDI_SIZE]; |
| uint8_t cdi_seal[DICE_CDI_SIZE]; |
| uint8_t certificate[kFakeCertSize + 10]; |
| size_t certificate_size; |
| }; |
| |
| TEST(DiceTest, KnownAnswer) { |
| FakeDiceOps ops; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultOk, result); |
| EXPECT_EQ(0, memcmp(next_state.cdi_attest, |
| dice::test::kExpectedCdiAttest_ZeroInput, DICE_CDI_SIZE)); |
| EXPECT_EQ(0, memcmp(next_state.cdi_seal, |
| dice::test::kExpectedCdiSeal_ZeroInput, DICE_CDI_SIZE)); |
| EXPECT_EQ(kFakeCertSize, next_state.certificate_size); |
| } |
| |
| TEST(DiceTest, HashFail) { |
| FakeDiceOps ops; |
| ops.hash_result_ = kDiceResultPlatformError; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultPlatformError, result); |
| } |
| |
| TEST(DiceTest, KdfFail) { |
| FakeDiceOps ops; |
| ops.kdf_result_ = kDiceResultPlatformError; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultPlatformError, result); |
| } |
| |
| TEST(DiceTest, CertFail) { |
| FakeDiceOps ops; |
| ops.generate_certificate_result_ = kDiceResultPlatformError; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultPlatformError, result); |
| } |
| |
| TEST(DiceTest, CertTooSmall) { |
| FakeDiceOps ops; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| kFakeCertSize - 1, next_state.certificate, &next_state.certificate_size, |
| next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultBufferTooSmall, result); |
| EXPECT_EQ(next_state.certificate_size, kFakeCertSize); |
| } |
| |
| TEST(DiceTest, NoExtraneousOps) { |
| FakeDiceOps ops; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultOk, result); |
| // These are brittle, but can act as a good sanity check that we're not |
| // regressing in how many expensive operations we call. |
| EXPECT_LE(ops.hash_count_, 2); |
| EXPECT_LE(ops.kdf_count_, 4); |
| EXPECT_LE(ops.generate_certificate_count_, 1); |
| } |
| |
| TEST(DiceTest, NoCertParamsPreservesCDIs) { |
| FakeDiceOps ops; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceStateForTest next_state_no_cert = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| sizeof(next_state.certificate), next_state.certificate, |
| &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); |
| result = DiceMainFlow( |
| &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, 0, |
| NULL, NULL, next_state_no_cert.cdi_attest, next_state_no_cert.cdi_seal); |
| EXPECT_EQ(kDiceResultOk, result); |
| EXPECT_EQ(0, memcmp(next_state.cdi_attest, next_state_no_cert.cdi_attest, |
| DICE_CDI_SIZE)); |
| EXPECT_EQ(0, memcmp(next_state.cdi_seal, next_state_no_cert.cdi_seal, |
| DICE_CDI_SIZE)); |
| } |
| |
| } // namespace |