| // 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 <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <memory> |
| |
| #include "dice/dice.h" |
| #include "dice/known_test_values.h" |
| #include "dice/test_framework.h" |
| #include "dice/test_utils.h" |
| #include "dice/utils.h" |
| #include "pw_string/format.h" |
| |
| namespace { |
| |
| using dice::test::CertificateType_Cbor; |
| using dice::test::DeriveFakeInputValue; |
| using dice::test::DiceStateForTest; |
| using dice::test::KeyType_Ed25519; |
| |
| TEST(DiceOpsTest, KnownAnswerZeroInput) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| NULL, 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); |
| // Both CDI values and the certificate should be deterministic. |
| 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)); |
| ASSERT_EQ(sizeof(dice::test::kExpectedCborEd25519Cert_ZeroInput), |
| next_state.certificate_size); |
| EXPECT_EQ(0, memcmp(dice::test::kExpectedCborEd25519Cert_ZeroInput, |
| next_state.certificate, next_state.certificate_size)); |
| } |
| |
| TEST(DiceOpsTest, KnownAnswerHashOnlyInput) { |
| DiceStateForTest current_state = {}; |
| DeriveFakeInputValue("cdi_attest", DICE_CDI_SIZE, current_state.cdi_attest); |
| DeriveFakeInputValue("cdi_seal", DICE_CDI_SIZE, current_state.cdi_seal); |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DeriveFakeInputValue("code_hash", DICE_HASH_SIZE, input_values.code_hash); |
| DeriveFakeInputValue("authority_hash", DICE_HASH_SIZE, |
| input_values.authority_hash); |
| input_values.config_type = kDiceConfigTypeInline; |
| DeriveFakeInputValue("inline_config", DICE_INLINE_CONFIG_SIZE, |
| input_values.config_value); |
| |
| DiceResult result = DiceMainFlow( |
| NULL, 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); |
| // Both CDI values and the certificate should be deterministic. |
| EXPECT_EQ( |
| 0, memcmp(next_state.cdi_attest, |
| dice::test::kExpectedCdiAttest_HashOnlyInput, DICE_CDI_SIZE)); |
| EXPECT_EQ( |
| 0, memcmp(next_state.cdi_seal, dice::test::kExpectedCdiSeal_HashOnlyInput, |
| DICE_CDI_SIZE)); |
| ASSERT_EQ(sizeof(dice::test::kExpectedCborEd25519Cert_HashOnlyInput), |
| next_state.certificate_size); |
| EXPECT_EQ(0, memcmp(dice::test::kExpectedCborEd25519Cert_HashOnlyInput, |
| next_state.certificate, next_state.certificate_size)); |
| } |
| |
| TEST(DiceOpsTest, WithCodeDescriptor) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| uint8_t descriptor[] = {0, 1, 2, 3}; |
| input_values.code_descriptor = descriptor; |
| input_values.code_descriptor_size = sizeof(descriptor); |
| DiceResult result = DiceMainFlow( |
| NULL, 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(kDiceResultInvalidInput, result); |
| } |
| |
| TEST(DiceOpsTest, WithConfigDescriptor) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| uint8_t descriptor[] = {0, 1, 2, 3}; |
| input_values.config_descriptor = descriptor; |
| input_values.config_descriptor_size = sizeof(descriptor); |
| input_values.config_type = kDiceConfigTypeDescriptor; |
| DiceResult result = DiceMainFlow( |
| NULL, 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(kDiceResultInvalidInput, result); |
| } |
| |
| TEST(DiceOpsTest, WithAuthorityDescriptor) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| uint8_t descriptor[] = {0, 1, 2, 3}; |
| input_values.authority_descriptor = descriptor; |
| input_values.authority_descriptor_size = sizeof(descriptor); |
| DiceResult result = DiceMainFlow( |
| NULL, 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(kDiceResultInvalidInput, result); |
| } |
| |
| TEST(DiceOpsTest, NonZeroMode) { |
| constexpr size_t kModeOffsetInCert = 315; |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| input_values.mode = kDiceModeDebug; |
| DiceResult result = DiceMainFlow( |
| NULL, 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(kDiceModeDebug, next_state.certificate[kModeOffsetInCert]); |
| } |
| |
| TEST(DiceOpsTest, SmallCertBuffer) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| DiceResult result = DiceMainFlow( |
| NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, |
| 12 /*too small*/, next_state.certificate, &next_state.certificate_size, |
| next_state.cdi_attest, next_state.cdi_seal); |
| EXPECT_EQ(kDiceResultBufferTooSmall, result); |
| EXPECT_EQ(sizeof(dice::test::kExpectedCborEd25519Cert_ZeroInput), |
| next_state.certificate_size); |
| } |
| |
| TEST(DiceOpsTest, InvalidConfigType) { |
| DiceStateForTest current_state = {}; |
| DiceStateForTest next_state = {}; |
| DiceInputValues input_values = {}; |
| input_values.config_type = (DiceConfigType)55; |
| DiceResult result = DiceMainFlow( |
| NULL, 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(kDiceResultInvalidInput, result); |
| } |
| |
| TEST(DiceOpsTest, PartialCertChain) { |
| constexpr size_t kNumLayers = 7; |
| DiceStateForTest states[kNumLayers + 1] = {}; |
| DiceInputValues inputs[kNumLayers] = {}; |
| for (size_t i = 0; i < kNumLayers; ++i) { |
| char seed[40]; |
| pw::string::Format(seed, "code_hash_%zu", i); |
| DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].code_hash); |
| pw::string::Format(seed, "authority_hash_%zu", i); |
| DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].authority_hash); |
| inputs[i].config_type = kDiceConfigTypeInline; |
| pw::string::Format(seed, "inline_config_%zu", i); |
| DeriveFakeInputValue(seed, DICE_INLINE_CONFIG_SIZE, inputs[i].config_value); |
| inputs[i].mode = kDiceModeNormal; |
| EXPECT_EQ( |
| kDiceResultOk, |
| DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal, |
| &inputs[i], sizeof(states[i + 1].certificate), |
| states[i + 1].certificate, &states[i + 1].certificate_size, |
| states[i + 1].cdi_attest, states[i + 1].cdi_seal)); |
| char suffix[40]; |
| pw::string::Format(suffix, "part_cert_chain_%zu", i); |
| } |
| // Use the first derived CDI cert as the 'root' of partial chain. |
| EXPECT_TRUE(dice::test::VerifyCertificateChain( |
| CertificateType_Cbor, states[1].certificate, states[1].certificate_size, |
| &states[2], kNumLayers - 1, /*is_partial_chain=*/true)); |
| } |
| |
| TEST(DiceOpsTest, FullCertChain) { |
| constexpr size_t kNumLayers = 7; |
| DiceStateForTest states[kNumLayers + 1] = {}; |
| DiceInputValues inputs[kNumLayers] = {}; |
| for (size_t i = 0; i < kNumLayers; ++i) { |
| char seed[40]; |
| pw::string::Format(seed, "code_hash_%zu", i); |
| DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].code_hash); |
| pw::string::Format(seed, "authority_hash_%zu", i); |
| DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].authority_hash); |
| inputs[i].config_type = kDiceConfigTypeInline; |
| pw::string::Format(seed, "inline_config_%zu", i); |
| DeriveFakeInputValue(seed, DICE_INLINE_CONFIG_SIZE, inputs[i].config_value); |
| inputs[i].mode = kDiceModeNormal; |
| EXPECT_EQ( |
| kDiceResultOk, |
| DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal, |
| &inputs[i], sizeof(states[i + 1].certificate), |
| states[i + 1].certificate, &states[i + 1].certificate_size, |
| states[i + 1].cdi_attest, states[i + 1].cdi_seal)); |
| char suffix[40]; |
| pw::string::Format(suffix, "full_cert_chain_%zu", i); |
| } |
| // Use a fake self-signed UDS cert as the 'root'. |
| uint8_t root_certificate[dice::test::kTestCertSize]; |
| size_t root_certificate_size = 0; |
| dice::test::CreateFakeUdsCertificate( |
| NULL, states[0].cdi_attest, CertificateType_Cbor, KeyType_Ed25519, |
| root_certificate, &root_certificate_size); |
| EXPECT_TRUE(dice::test::VerifyCertificateChain( |
| CertificateType_Cbor, root_certificate, root_certificate_size, &states[1], |
| kNumLayers, /*is_partial_chain=*/false)); |
| } |
| |
| } // namespace |