blob: 3539c4dec28ac41f73933c658178c8377f10dec9 [file] [log] [blame]
/*
* Copyright 2020, The Android Open Source Project
*
* 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
*
* http://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 <gtest/gtest.h>
#include "km_compat.h"
#include <keymint_support/keymint_tags.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/keymint/IKeyMintOperation.h>
using ::aidl::android::hardware::security::keymint::Algorithm;
using ::aidl::android::hardware::security::keymint::BlockMode;
using ::aidl::android::hardware::security::keymint::Certificate;
using ::aidl::android::hardware::security::keymint::Digest;
using ::aidl::android::hardware::security::keymint::ErrorCode;
using ::aidl::android::hardware::security::keymint::IKeyMintOperation;
using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
using ::aidl::android::hardware::security::keymint::KeyPurpose;
using ::aidl::android::hardware::security::keymint::PaddingMode;
using ::aidl::android::hardware::security::keymint::SecurityLevel;
namespace KMV1 = ::aidl::android::hardware::security::keymint;
static std::vector<uint8_t> generateAESKey(std::shared_ptr<KeyMintDevice> device) {
auto keyParams = std::vector<KeyParameter>({
KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::AES),
KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 128),
KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC),
KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE),
KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::DECRYPT),
});
KeyCreationResult creationResult;
auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
if (!status.isOk()) {
return {};
}
return creationResult.keyBlob;
}
static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
bool valid) {
auto blob = generateAESKey(device);
std::vector<KeyParameter> kps;
if (valid) {
kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC));
kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE));
}
BeginResult beginResult;
auto status = device->begin(KeyPurpose::ENCRYPT, blob, kps, HardwareAuthToken(), &beginResult);
if (!status.isOk()) {
return status;
}
return beginResult;
}
static const int NUM_SLOTS = 2;
TEST(SlotTest, TestSlots) {
static std::shared_ptr<KeyMintDevice> device =
KeyMintDevice::getWrappedKeymasterDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
ASSERT_NE(device.get(), nullptr);
device->setNumFreeSlots(NUM_SLOTS);
// A begin() that returns a failure should not use a slot.
auto result = begin(device, false);
ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
// Fill up all the slots.
std::vector<std::shared_ptr<IKeyMintOperation>> operations;
for (int i = 0; i < NUM_SLOTS; i++) {
auto result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
operations.push_back(std::get<BeginResult>(result).operation);
}
// We should not be able to create a new operation.
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
// TODO: I'm not sure how to generate a failing update call to test that.
// Calling finish should free up a slot.
auto last = operations.back();
operations.pop_back();
std::vector<uint8_t> byteVec;
auto status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
std::nullopt /* authToken */, std::nullopt /* timestampToken */,
std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(status.isOk());
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
operations.push_back(std::get<BeginResult>(result).operation);
// Calling finish and abort on an already-finished operation should not free up another slot.
status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
std::nullopt /* authToken */, std::nullopt /* timestampToken */,
std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(!status.isOk());
status = last->abort();
ASSERT_TRUE(!status.isOk());
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
// Calling abort should free up a slot.
last = operations.back();
operations.pop_back();
status = last->abort();
ASSERT_TRUE(status.isOk());
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
operations.push_back(std::get<BeginResult>(result).operation);
// Calling finish and abort on an already-aborted operation should not free up another slot.
status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
std::nullopt /* authToken */, std::nullopt /* timestampToken */,
std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(!status.isOk());
status = last->abort();
ASSERT_TRUE(!status.isOk());
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
// Generating a certificate with signWith uses a slot but falls back to not using one.
auto kps = std::vector<KeyParameter>({
KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::RSA),
KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 2048),
KMV1::makeKeyParameter(KMV1::TAG_RSA_PUBLIC_EXPONENT, 65537),
KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, 0),
KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, 253402300799000),
KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
});
KeyCreationResult creationResult;
status = device->generateKey(kps, std::nullopt /* attest_key */, &creationResult);
ASSERT_TRUE(status.isOk());
// But generating a certificate with signCert does not use a slot.
kps.pop_back();
status = device->generateKey(kps, std::nullopt /* attest_key */, &creationResult);
ASSERT_TRUE(status.isOk());
// Destructing operations should free up their slots.
operations.clear();
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
}