blob: b7a34d93c63f0d859f99d811317196e1d5aa229b [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
//
// 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 an effective 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