Files
android_bootable_recovery/crypto/fscrypt/Keymaster.cpp
bigbiff bbbfe171f1 fscrypt: updates for wrapped key
- During OTA upgrades if security state or ROT changes then Keymaster
keys requires upgrade. So for such usescases, if the FBE ephemeral
key export fails, check whether KM key requires upgrade and try for
exporting ephemeral key again.

CRs-Fixed: 2632902
Change-Id: I3ee2fcd97a56b628dc4304867c8f2b8da875f883
Signed-off-by: Neeraj Soni <neersoni@codeaurora.org>

- Commit 77df7f2 / http://aosp/1217657 ("Refactor to use
EncryptionPolicy everywhere we used to use raw_ref") unintentionally
made fscrypt_initialize_systemwide_keys() start specifying keepOld=true
(via default parameter value) when retrieving the system DE key, and
likewise for read_or_create_volkey() and volume keys.

As a result, if the associated Keymaster key needs to be upgraded, the
upgraded key blob gets written to "keymaster_key_blob_upgraded", but it
doesn't replace the original "keymaster_key_blob", nor is the original
key deleted from Keymaster.  This happens at every boot, eventually
resulting in the RPMB partition in Keymaster becoming full.

Only the metadata encryption key ever needs keepOld=true, since it's the
only key that isn't stored in /data, and the purpose of keepOld=true is
to allow a key that isn't stored in /data to be committed or rolled back
when a userdata checkpoint is committed or rolled back.

So, fix this bug by removing the default value of keepOld, and
specifying false everywhere except the metadata encryption key.

Note that when an affected device gets this fix, it will finally upgrade
its system DE key correctly.  However, this fix doesn't free up space in
Keymaster that was consumed by this bug.

Test: On bramble:
  - Flashed rvc-d1-dev build, with wiping userdata
  - Flashed a newer build, without wiping userdata
  - Log expectedly shows key upgrades:
        $ adb logcat | grep 'Upgrading key'
        D vold    : Upgrading key:
/metadata/vold/metadata_encryption/key
        D vold    : Upgrading key: /data/unencrypted/key
        D vold    : Upgrading key: /data/misc/vold/user_keys/de/0
        D vold    : Upgrading key:
/data/misc/vold/user_keys/ce/0/current
  - Rebooted
  - Log unexpectedly shows the system DE key being upgraded again:
        $ adb logcat | grep 'Upgrading key'
        D vold    : Upgrading key: /data/unencrypted/key
  - "keymaster_key_blob_upgraded" unexpectedly still exists:
        $ adb shell find /data /metadata -name
keymaster_key_blob_upgraded
        /data/unencrypted/key/keymaster_key_blob_upgraded
  - Applied this fix and flashed, without wiping userdata
  - Log shows system DE key being upgraded (expected because due to the
    bug, the upgraded key didn't replace the original one before)
        $ adb logcat | grep 'Upgrading key'
        D vold    : Upgrading key: /data/unencrypted/key
  - "keymaster_key_blob_upgraded" expectedly no longer exists
        $ adb shell find /data /metadata -name
keymaster_key_blob_upgraded
  - Rebooted
  - Log expectedly doesn't show any more key upgrades
        $ adb logcat | grep 'Upgrading key'
Bug: 171944521
Bug: 172019387
(cherry picked from commit c493903732d0c17b33091cf722cbcc3262292801)
Merged-In: I42d3f5fbe32cb2ec229f4b614cfb271412a3ed29
Change-Id: I42d3f5fbe32cb2ec229f4b614cfb271412a3ed29

Change-Id: I0449b812e91c13020a8b653f2149c33e46027b97
2021-06-25 18:42:20 -04:00

378 lines
14 KiB
C++
Executable File

/*
* Copyright (C) 2016 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 "Keymaster.h"
#include <android-base/logging.h>
#include <keymasterV4_1/authorization_set.h>
#include <keymasterV4_1/keymaster_utils.h>
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::keymaster::V4_0::SecurityLevel;
KeymasterOperation::~KeymasterOperation() {
if (mDevice) mDevice->abort(mOpHandle);
}
bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
const std::function<void(const char*, size_t)> consumer) {
uint32_t inputConsumed = 0;
km::ErrorCode km_error;
auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
const hidl_vec<km::KeyParameter>& /*ignored*/,
const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
inputConsumed += inputConsumedDelta;
consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
while (inputConsumed != inputLen) {
size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
auto inputBlob = km::support::blob2hidlVec(
reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
if (!error.isOk()) {
LOG(ERROR) << "update failed: " << error.description();
mDevice = nullptr;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "update failed, code " << int32_t(km_error);
mDevice = nullptr;
return false;
}
if (inputConsumed > inputLen) {
LOG(ERROR) << "update reported too much input consumed";
mDevice = nullptr;
return false;
}
}
return true;
}
bool KeymasterOperation::finish(std::string* output) {
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
hidl_vec<uint8_t>(), km::HardwareAuthToken(),
km::VerificationToken(), hidlCb);
mDevice = nullptr;
if (!error.isOk()) {
LOG(ERROR) << "finish failed: " << error.description();
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "finish failed, code " << int32_t(km_error);
return false;
}
return true;
}
/* static */ bool Keymaster::hmacKeyGenerated = false;
Keymaster::Keymaster() {
auto devices = KmDevice::enumerateAvailableDevices();
if (!hmacKeyGenerated) {
KmDevice::performHmacKeyAgreement(devices);
hmacKeyGenerated = true;
}
for (auto& dev : devices) {
// Do not use StrongBox for device encryption / credential encryption. If a security chip
// is present it will have Weaver, which already strengthens CE. We get no additional
// benefit from using StrongBox here, so skip it.
if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
mDevice = std::move(dev);
break;
}
}
if (!mDevice) return;
auto& version = mDevice->halVersion();
LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
<< " for encryption. Security level: " << toString(version.securityLevel)
<< ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName();
}
bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
const km::KeyCharacteristics& /*ignored*/) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
};
auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "generate_key failed: " << error.description();
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
return false;
}
return true;
}
km::ErrorCode Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) {
auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (key)
key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]), exportedKeyBlob.size());
};
auto error = mDevice->exportKey(km::KeyFormat::RAW, kmKeyBlob, {}, {}, hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "export_key failed: " << error.description();
return km::ErrorCode::UNKNOWN_ERROR;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
return km_error;
}
return km::ErrorCode::OK;
}
bool Keymaster::deleteKey(const std::string& key) {
auto keyBlob = km::support::blob2hidlVec(key);
auto error = mDevice->deleteKey(keyBlob);
if (!error.isOk()) {
LOG(ERROR) << "delete_key failed: " << error.description();
return false;
}
if (error != km::ErrorCode::OK) {
LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
return false;
}
return true;
}
bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
std::string* newKey) {
auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (newKey)
newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
upgradedKeyBlob.size());
};
auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "upgrade_key failed: " << error.description();
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
return false;
}
return true;
}
KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
const km::AuthorizationSet& inParams,
const km::HardwareAuthToken& authToken,
km::AuthorizationSet* outParams) {
auto keyBlob = km::support::blob2hidlVec(key);
uint64_t mOpHandle;
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
uint64_t operationHandle) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (outParams) *outParams = _outParams;
mOpHandle = operationHandle;
};
auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "begin failed: " << error.description();
return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "begin failed, code " << int32_t(km_error);
return KeymasterOperation(km_error);
}
return KeymasterOperation(mDevice.get(), mOpHandle);
}
bool Keymaster::isSecure() {
return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
}
void Keymaster::earlyBootEnded() {
auto devices = KmDevice::enumerateAvailableDevices();
for (auto& dev : devices) {
auto error = dev->earlyBootEnded();
if (!error.isOk()) {
LOG(ERROR) << "earlyBootEnded call failed: " << error.description() << " for "
<< dev->halVersion().keymasterName;
}
km::V4_1_ErrorCode km_error = error;
if (km_error != km::V4_1_ErrorCode::OK && km_error != km::V4_1_ErrorCode::UNIMPLEMENTED) {
LOG(ERROR) << "Error reporting early boot ending to keymaster: "
<< static_cast<int32_t>(km_error) << " for "
<< dev->halVersion().keymasterName;
}
}
}
int keymaster_compatibility_cryptfs_scrypt() {
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
return dev.isSecure();
}
static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
uint32_t* out_size) {
if (!buffer || !out_size) {
LOG(ERROR) << "Missing target pointers";
return false;
}
*out_size = towrite.size();
if (buffer_size < towrite.size()) {
LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
return false;
}
memset(buffer, '\0', buffer_size);
std::copy(towrite.begin(), towrite.end(), buffer);
return true;
}
static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit) {
return km::AuthorizationSetBuilder()
.RsaSigningKey(rsa_key_size, rsa_exponent)
.NoDigestOrPadding()
.Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
.Authorization(km::TAG_NO_AUTH_REQUIRED)
.Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
}
int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size) {
if (key_out_size) {
*key_out_size = 0;
}
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
std::string key;
if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
return 0;
}
int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, const uint8_t* key_blob,
size_t key_blob_size, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size) {
if (key_out_size) {
*key_out_size = 0;
}
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
std::string new_key;
if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
return -1;
if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
return 0;
}
KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return KeymasterSignResult::error;
}
if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
return KeymasterSignResult::error;
}
km::AuthorizationSet outParams;
std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
std::string input(reinterpret_cast<const char*>(object), object_size);
std::string output;
KeymasterOperation op;
auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
while (true) {
op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
sleep(ratelimit);
continue;
} else
break;
}
if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
LOG(ERROR) << "Keymaster key requires upgrade";
return KeymasterSignResult::upgrade;
}
if (op.errorCode() != km::ErrorCode::OK) {
LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
return KeymasterSignResult::error;
}
if (!op.updateCompletely(input, &output)) {
LOG(ERROR) << "Error sending data to keymaster signature transaction: "
<< uint32_t(op.errorCode());
return KeymasterSignResult::error;
}
if (!op.finish(&output)) {
LOG(ERROR) << "Error finalizing keymaster signature transaction: "
<< int32_t(op.errorCode());
return KeymasterSignResult::error;
}
*signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
if (*signature_buffer == nullptr) {
LOG(ERROR) << "Error allocation buffer for keymaster signature";
return KeymasterSignResult::error;
}
*signature_buffer_size = output.size();
std::copy(output.data(), output.data() + output.size(), *signature_buffer);
return KeymasterSignResult::ok;
}