| /* |
| * Copyright (C) 2021 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 "trusty_secure_deletion_secret_storage.h" |
| |
| #include <array> |
| #include <optional> |
| #include <vector> |
| |
| #include <lib/storage/storage.h> |
| #include <uapi/err.h> |
| |
| #include <keymaster/logger.h> |
| #include <keymaster/random_source.h> |
| |
| namespace keymaster { |
| |
| namespace { |
| |
| // Maximum number of attempts to perform a secure storage transaction to read or |
| // delete a secure deletion secret. Because the storageproxy may be restarted |
| // while this code is running, it may be necessary to retry. But because it's |
| // unclear exactly what error codes may be returned when the proxy is shut down, |
| // we conservatively retry all unexpected errors. To avoid an infinite loop, we |
| // set a limit on the number of retries (though hitting the limit and returning |
| // an error will likely break the boot anyway). Ideally, we should never need |
| // more than one retry. We allow three. |
| constexpr size_t kMaxTries = 3; |
| |
| // Name of the file to store secrets. The "_1" suffix is to allow for new file |
| // formats/versions in the future. |
| constexpr char kSecureDeletionSecretFileName[] = "SecureDeletionSecrets_1"; |
| |
| // Each secret is 16 bytes. |
| constexpr storage_off_t kSecretSize = 16; |
| |
| // The factory reset secret is composed of two secrets, so 32 bytes, and it's |
| // stored at offset 0. |
| constexpr storage_off_t kFactoryResetSecretSize = kSecretSize * 2; |
| constexpr storage_off_t kFactoryResetSecretPos = 0; |
| constexpr storage_off_t kFirstSecureDeletionSecretPos = |
| kFactoryResetSecretPos + kFactoryResetSecretSize; |
| |
| // We read secrets in blocks of 32, so 512 bytes. |
| constexpr storage_off_t kBlockSize = kSecretSize * 32; |
| |
| // Limit file size to 16 KiB (except for key upgrades, see |
| // kMaxSecretFileSizeForUpgrades). |
| constexpr storage_off_t kMaxSecretFileSize = kBlockSize * 32; |
| |
| // This is a higher file size limit, with the space above kMaxSecretFileSize |
| // usable only for key IDs that need to be written as part of a key upgrade. |
| // This is to reduce the probability that keys are degraded as a result of |
| // upgrading. |
| constexpr storage_off_t kMaxSecretFileSizeForUpgrades = |
| kMaxSecretFileSize + 8 * kBlockSize; |
| |
| // We set a bit in the first byte of each slot to indicate that the slot is in |
| // use. This reduces the maximum entropy of each slot to 127 bits. |
| constexpr uint8_t kInUseFlag = 0x80; |
| |
| /** |
| * StorageFile represents a secure storage file, and provides operations on it. |
| * Use StorageSession::OpenFile to create a StorageFile. |
| */ |
| class StorageFile { |
| public: |
| StorageFile(const StorageFile&) = delete; |
| StorageFile& operator=(const StorageFile&) = delete; |
| |
| StorageFile(StorageFile&& rhs) |
| : fileHandle_(std::move(rhs.fileHandle_)), |
| fileSize_(rhs.fileSize_) { |
| rhs.fileHandle_ = std::nullopt; |
| rhs.fileSize_ = 0; |
| } |
| |
| ~StorageFile() { CloseFile(); } |
| |
| /** |
| * Close the file. |
| * |
| * Note that normally it's not necessary to call this, because the dtor |
| * will. |
| */ |
| void CloseFile() { |
| if (!fileHandle_) { |
| return; |
| } |
| |
| LOG_D("Closing file handle %llu", *fileHandle_); |
| storage_close_file(*fileHandle_); |
| fileHandle_ = std::nullopt; |
| } |
| |
| /** |
| * Return size of file. |
| */ |
| storage_off_t size() const { return fileSize_; } |
| |
| /** |
| * Reads a block of bytes from the file. On error returns std::nullopt. |
| */ |
| std::optional<Buffer> ReadBlock(storage_off_t readPos, |
| storage_off_t bytesToRead) const { |
| if (!fileHandle_) { |
| return std::nullopt; |
| } |
| |
| Buffer buf(bytesToRead); |
| if (buf.buffer_size() < bytesToRead) { |
| LOG_E("Error memory allocation failed trying to allocate ReadBlock buffer.", |
| 0); |
| return std::nullopt; |
| } |
| |
| ssize_t bytesRead = storage_read( |
| *fileHandle_, readPos, buf.peek_write(), buf.available_write()); |
| if (bytesRead < 0) { |
| LOG_E("Error %zd reading file", bytesRead); |
| return std::nullopt; |
| } else if (static_cast<size_t>(bytesRead) < bytesToRead) { |
| LOG_E("Error attempt to read %llu bytes returned only %zd bytes", |
| bytesToRead, bytesRead); |
| return std::nullopt; |
| } |
| |
| if (!buf.advance_write(bytesRead)) { |
| LOG_E("Failed to update buffer write position. Code error.", 0); |
| return std::nullopt; |
| } |
| return buf; |
| } |
| |
| /** |
| * Write a block of bytes from `data` of size `size` to storage at offset |
| * `pos`. Will not extend the file. |
| * |
| * Returns false if the write would go past the end of the file, or if an |
| * error occurs (all errors are logged). |
| */ |
| bool WriteBlock(storage_off_t pos, const uint8_t* data, size_t size) const { |
| if (!fileHandle_) { |
| LOG_E("Attempt to write to invalid file handle", 0); |
| return false; |
| } |
| |
| storage_off_t end; |
| if (__builtin_add_overflow(pos, size, &end) || end > fileSize_) { |
| LOG_E("Attempt to write past EOF", 0); |
| return false; |
| } |
| |
| ssize_t bytesWritten = |
| storage_write(*fileHandle_, pos, data, size, 0 /* opflags */); |
| if (bytesWritten < 0) { |
| LOG_E("Error %zd writing rollback record at offset %llu", |
| bytesWritten, pos); |
| return false; |
| } else if (static_cast<size_t>(bytesWritten) < size) { |
| LOG_E("Wrote %zd of %zu bytes", bytesWritten, size); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Resize the file to `newSize` bytes. |
| * |
| * Returns error code from uapi/err.h. Errors are logged. |
| */ |
| int Resize(storage_off_t newSize) { |
| if (!fileHandle_) { |
| LOG_E("Attempt to resize invalid file handle", 0); |
| return -ERR_NOT_VALID; |
| } |
| |
| int rc = storage_set_file_size(*fileHandle_, newSize, 0 /* opflags */); |
| if (rc) { |
| LOG_E("Error %d resizing file from %llu to %llu", rc, fileSize_, |
| newSize); |
| return rc; |
| } |
| |
| fileSize_ = newSize; |
| return NO_ERROR; |
| } |
| |
| private: |
| friend class StorageSession; |
| |
| StorageFile(file_handle_t fileHandle, storage_off_t fileSize) |
| : fileHandle_(fileHandle), fileSize_(fileSize) {} |
| |
| std::optional<file_handle_t> fileHandle_; |
| storage_off_t fileSize_; |
| }; |
| |
| /** |
| * StorageSession represents a secure storage session, and provides methods to |
| * manipulate files within the session, and to finalize a storage transaction. |
| */ |
| class StorageSession { |
| public: |
| // Error codes used by DeleteFile. |
| enum class Error : uint32_t { |
| OK = 0, |
| NOT_FOUND = 1, |
| UNKNOWN = 2, |
| }; |
| |
| StorageSession(const StorageSession&) = delete; |
| StorageSession(StorageSession&& rhs) : session_(rhs.session_) { |
| rhs.session_ = STORAGE_INVALID_SESSION; |
| } |
| ~StorageSession() { |
| if (session_ != STORAGE_INVALID_SESSION) { |
| LOG_D("Closing storage session %llu", session_); |
| storage_close_session(session_); |
| } |
| } |
| |
| /** |
| * Creates a session, connected to the specified port. |
| * |
| * There is a possibility that the port has not been created when this |
| * method is called. In this case its behavior is determined by the |
| * `waitForPort` argument: |
| * |
| * - if true, the method will block until the port is available and the |
| * connection is completed. |
| * |
| * - if false, the method will return std::nullopt immediately. |
| * |
| * If the port exists, this method will block until the connection is |
| * completed. |
| */ |
| static std::optional<StorageSession> CreateSession( |
| bool waitForPort = true, |
| const char* port = STORAGE_CLIENT_TP_PORT) { |
| LOG_D("Opening storage session on port %s (wait: %s)", port, |
| waitForPort ? "true" : "false"); |
| // We use connect rather than storage_open_session because the latter |
| // always waits for the port. |
| long rc = connect(port, waitForPort ? IPC_CONNECT_WAIT_FOR_PORT : 0); |
| if (rc < 0) { |
| LOG_E("Error %ld opening storage session on port %s.", rc, port); |
| return std::nullopt; |
| } |
| storage_session_t session = static_cast<storage_session_t>(rc); |
| LOG_D("Opened storage session %llu", session); |
| return StorageSession(session); |
| } |
| |
| /** |
| * Open a file. Returns std::nullopt on failure (after logging error code). |
| */ |
| std::optional<StorageFile> OpenFile( |
| const char* fileName, |
| uint32_t flags = STORAGE_FILE_OPEN_CREATE) const { |
| file_handle_t fileHandle; |
| int err = storage_open_file(session_, &fileHandle, fileName, flags, |
| 0 /* opflags */); |
| if (err) { |
| LOG_E("Error %d opening file %s", err, fileName); |
| return std::nullopt; |
| } |
| LOG_D("Opened file %s with handle %llu", fileName, fileHandle); |
| |
| storage_off_t fileSize; |
| err = storage_get_file_size(fileHandle, &fileSize); |
| if (err) { |
| LOG_E("Error %d reading size of file %s", err, fileName); |
| storage_close_file(fileHandle); |
| return std::nullopt; |
| } |
| return StorageFile(fileHandle, fileSize); |
| } |
| |
| /** |
| * Delete a file. |
| * |
| * Returns Error::OK on success, Error::NOT_FOUND, if the file did not |
| * exist, Error::UNKNOWN in any other case. |
| */ |
| Error DeleteFile(const char* fileName) const { |
| int rc = storage_delete_file(session_, fileName, 0 /* opflags */); |
| if (rc < 0) { |
| LOG_E("Error (%d) deleting file %s", rc, fileName); |
| if (rc == ERR_NOT_FOUND) { |
| return Error::NOT_FOUND; |
| } else { |
| return Error::UNKNOWN; |
| } |
| } |
| return Error::OK; |
| } |
| |
| /** |
| * Ends current transaction (any uncommitted changes in this session), |
| * either committing or aborting (rolling back) the changes, as specified by |
| * `commit`. |
| * |
| * Note failing to commit the transaction will result in it being lost. |
| * |
| * Returns true on success, false on failure. |
| */ |
| bool EndTransaction(bool commit) { |
| int rc = storage_end_transaction(session_, commit); |
| if (rc < 0) { |
| LOG_E("Error (%d) committing transaction", rc); |
| return false; |
| } |
| return true; |
| } |
| |
| private: |
| StorageSession(storage_session_t session) : session_(session) {} |
| |
| storage_session_t session_; |
| }; |
| |
| /** |
| * Zeros each secret from position `begin` to position `end`. |
| */ |
| bool zero_entries(const StorageFile& file, |
| storage_off_t begin, |
| storage_off_t end) { |
| if (begin % kSecretSize != 0) { |
| LOG_S("zero_entries called with invalid offset %llu", begin); |
| return false; |
| } |
| |
| for (storage_off_t pos = begin; pos < end; pos += kSecretSize) { |
| uint8_t zero_buf[kSecretSize] = {0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| if (!file.WriteBlock(pos, zero_buf, sizeof(zero_buf))) { |
| LOG_E("Failed to zero secret at offset %llu", pos); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Finds an empty slot in file. Returns empty on error, 0 on failure to |
| * find an empty slot and a valid (>0) slot number otherwise. |
| */ |
| std::optional<uint32_t /* keySlot */> find_empty_slot(const StorageFile& file, |
| bool isUpgrade) { |
| storage_off_t end = |
| std::min(file.size(), isUpgrade ? kMaxSecretFileSizeForUpgrades |
| : kMaxSecretFileSize); |
| |
| uint32_t retval = 0; |
| for (storage_off_t filePos = 0; filePos < end; filePos += kBlockSize) { |
| std::optional<Buffer> block = file.ReadBlock(filePos, kBlockSize); |
| if (!block) { |
| LOG_E("Failed to read block of secrets", 0); |
| return std::nullopt; |
| } |
| |
| size_t blockPos = |
| filePos == kFactoryResetSecretPos ? kFactoryResetSecretSize : 0; |
| for (; blockPos < block->available_read(); blockPos += kSecretSize) { |
| uint8_t first_byte = *(block->begin() + blockPos); |
| if ((first_byte & kInUseFlag) == 0 && retval == 0) { |
| static_assert(kBlockSize % kSecretSize == 0 && |
| kFactoryResetSecretSize % kSecretSize == 0); |
| retval = static_cast<uint32_t>((filePos + blockPos) / |
| kSecretSize); |
| } |
| } |
| } |
| |
| return retval; |
| } |
| |
| // Helper class that calls the provided function (probably a lambda) on |
| // destruction if not disarmed. |
| template <typename F> |
| class OnExit { |
| public: |
| OnExit(F f) : f_(f) {} |
| ~OnExit() { |
| if (!disarmed_) { |
| f_(); |
| } |
| } |
| |
| void disarm() { disarmed_ = true; } |
| |
| private: |
| F f_; |
| bool disarmed_ = false; |
| }; |
| |
| } // namespace |
| |
| bool TrustySecureDeletionSecretStorage::LoadOrCreateFactoryResetSecret( |
| bool wait_for_port) const { |
| if (factory_reset_secret_) { |
| // Already read. |
| return true; |
| } |
| |
| LOG_D("Trying to open a session to read factory reset secret", 0); |
| std::optional<StorageSession> session = |
| StorageSession::CreateSession(wait_for_port); |
| if (!session) { |
| return false; |
| } |
| |
| LOG_D("Trying to open secure secrets file", 0); |
| std::optional<StorageFile> file = |
| session->OpenFile(kSecureDeletionSecretFileName); |
| if (!file) { |
| // This shouldn't be possible, unless maybe the session just went away? |
| LOG_E("Can't open secure secrets file.", 0); |
| return false; |
| } |
| |
| if (file->size() > 0) { |
| LOG_D("Opened non-empty secure secrets file.", 0); |
| std::optional<Buffer> block = file->ReadBlock(kFactoryResetSecretPos, |
| kFactoryResetSecretSize); |
| if (!block) { |
| LOG_E("Failed to read factory reset secret", 0); |
| return false; |
| } |
| |
| LOG_D("Read factory-reset secret, size %zu", block->available_read()); |
| factory_reset_secret_ = std::move(block); |
| return true; |
| } |
| |
| // The file was just created. Need to create the factory reset secret. |
| LOG_I("Created new secure secrets file, size %llu", file->size()); |
| if (file->Resize(kBlockSize) != NO_ERROR) { |
| LOG_E("Failed to grow new file from 0 to %llu bytes", kBlockSize); |
| return false; |
| } |
| LOG_D("Resized secure secrets file to size %llu", file->size()); |
| |
| static_assert(kBlockSize >= kFactoryResetSecretSize); |
| Buffer buf(kFactoryResetSecretSize); |
| keymaster_error_t error = |
| random_.GenerateRandom(buf.peek_write(), buf.available_write()); |
| if (error != KM_ERROR_OK || !buf.advance_write(kFactoryResetSecretSize)) { |
| LOG_E("Failed to generate %zu random bytes for factory reset secret", |
| kFactoryResetSecretSize); |
| return false; |
| } |
| |
| if (!file->WriteBlock(kFactoryResetSecretPos, buf.peek_read(), |
| buf.available_read())) { |
| LOG_E("Failed to write factory reset secret", 0); |
| return false; |
| } |
| LOG_D("Wrote new factory reset secret.", 0); |
| |
| if (!zero_entries(*file, kFirstSecureDeletionSecretPos /* begin */, |
| kBlockSize /* end */)) { |
| LOG_E("Failed to zero secure deletion secret entries in first block", |
| 0); |
| return false; |
| } |
| LOG_D("Zeroed secrets.", 0); |
| |
| if (!session->EndTransaction(true /* commit */)) { |
| LOG_E("Failed to commit transaction creating secure secrets file", 0); |
| return false; |
| } |
| LOG_D("Committed new secrets file.", 0); |
| |
| LOG_I("Got factory reset secret of size %zu", buf.buffer_size()); |
| factory_reset_secret_ = std::move(buf); |
| return true; |
| } |
| |
| std::optional<SecureDeletionData> |
| TrustySecureDeletionSecretStorage::CreateDataForNewKey(bool secure_deletion, |
| bool is_upgrade) const { |
| if (!LoadOrCreateFactoryResetSecret(false /* wait_for_port */) || |
| !factory_reset_secret_) { |
| // Unable to get factory reset secret from secure storage. |
| LOG_I("Unable to get factory reset secret", 0); |
| return std::nullopt; |
| } |
| |
| SecureDeletionData retval; |
| retval.factory_reset_secret.Reinitialize(*factory_reset_secret_); |
| |
| if (!secure_deletion) { |
| LOG_D("Secure deletion not requested.", 0); |
| return retval; |
| } |
| |
| retval.secure_deletion_secret.reserve(kSecretSize); |
| if (retval.secure_deletion_secret.buffer_size() == 0) { |
| return std::nullopt; |
| } |
| |
| keymaster_error_t error = random_.GenerateRandom( |
| retval.secure_deletion_secret.peek_write(), |
| retval.secure_deletion_secret.available_write()); |
| if (error != KM_ERROR_OK) { |
| // Ths really shouldn't be possible. Perhaps we should abort()? |
| LOG_E("Failed to create secure deletion secret", 0); |
| return std::nullopt; |
| } |
| retval.secure_deletion_secret.peek_write()[0] |= kInUseFlag; |
| retval.secure_deletion_secret.advance_write( |
| retval.secure_deletion_secret.available_write()); |
| |
| auto sds_cleanup = OnExit([&]() { retval.secure_deletion_secret.Clear(); }); |
| |
| std::optional<StorageSession> session = |
| StorageSession::CreateSession(); // Will block |
| |
| if (!session) { |
| LOG_E("Failed to open session in CreateDateForNewKey", 0); |
| return retval; |
| } |
| LOG_D("Opened session to store secure deletion secret.", 0); |
| |
| std::optional<StorageFile> file = |
| session->OpenFile(kSecureDeletionSecretFileName); |
| if (!file) { |
| LOG_E("Failed to open file in CreateDateForNewKey", 0); |
| return retval; |
| } |
| LOG_D("Opened file to store secure deletion secret.", 0); |
| |
| std::optional<uint32_t> keySlot = find_empty_slot(*file, is_upgrade); |
| if (!keySlot) { |
| LOG_E("Error while searching for key slot", 0); |
| return retval; |
| } |
| |
| if (*keySlot == 0) { |
| bool can_resize = |
| file->size() < kMaxSecretFileSize || |
| (is_upgrade && file->size() < kMaxSecretFileSizeForUpgrades); |
| |
| if (!can_resize) { |
| LOG_I("Didn't find a slot and can't grow the file larger than %llu", |
| file->size()); |
| return retval; |
| } |
| |
| storage_off_t old_size = file->size(); |
| LOG_D("Attempting to resize file from %llu to %llu", file->size(), |
| file->size() + kBlockSize); |
| int rc = file->Resize(old_size + kBlockSize); |
| if (rc != NO_ERROR) { |
| LOG_E("Failed (%d) to grow file to make room for a key slot", rc); |
| return retval; |
| } |
| LOG_D("Resized file to %llu", file->size()); |
| |
| if (!zero_entries(*file, old_size, file->size())) { |
| LOG_E("Error zeroing space in extended file", 0); |
| return retval; |
| } |
| |
| keySlot = old_size / kSecretSize; |
| } |
| |
| LOG_D("Writing new deletion secret to key slot %u", *keySlot); |
| if (!file->WriteBlock(*keySlot * kSecretSize, |
| retval.secure_deletion_secret.peek_read(), |
| retval.secure_deletion_secret.available_read())) { |
| LOG_E("Failed to write new deletion secret to key slot %u", *keySlot); |
| return retval; |
| } |
| |
| if (!session->EndTransaction(true /* commit */)) { |
| LOG_E("Failed to commit transaction writing new deletion secret to slot %u", |
| *keySlot); |
| return retval; |
| } |
| LOG_D("Committed new secret.", 0); |
| |
| sds_cleanup.disarm(); // Secure deletion secret written; no need to wipe. |
| retval.key_slot = *keySlot; |
| return retval; |
| } |
| |
| SecureDeletionData TrustySecureDeletionSecretStorage::GetDataForKey( |
| const uint32_t key_slot) const { |
| for (size_t tries = 0; tries < kMaxTries && !factory_reset_secret_; |
| ++tries) { |
| LoadOrCreateFactoryResetSecret(true /* waitForPort */); |
| } |
| if (!factory_reset_secret_) { |
| return SecureDeletionData{}; |
| } |
| |
| SecureDeletionData retval; |
| retval.key_slot = key_slot; |
| retval.factory_reset_secret.Reinitialize(*factory_reset_secret_); |
| |
| bool secureDeletionSecretRequested = (key_slot != 0); |
| if (!secureDeletionSecretRequested) { |
| LOG_D("Secure deletion not requested.", 0); |
| return retval; |
| } |
| |
| LOG_D("Need to read secure deletion secret from slot %u", retval.key_slot); |
| |
| for (size_t tries = 0; tries < kMaxTries; ++tries) { |
| std::optional<StorageSession> session = |
| StorageSession::CreateSession(); // Will block |
| if (!session) { |
| LOG_E("Failed to open session to get secure deletion data.", 0); |
| continue; |
| } |
| |
| std::optional<StorageFile> file = |
| session->OpenFile(kSecureDeletionSecretFileName); |
| if (!file) { |
| LOG_E("Failed to open file to get secure deletion data.", 0); |
| continue; |
| } |
| |
| storage_off_t keySlotBegin = retval.key_slot * kSecretSize; |
| storage_off_t keySlotEnd = keySlotBegin + kSecretSize; |
| if (keySlotEnd > file->size()) { |
| LOG_E("Invalid key slot %u would read past end of file of size %llu", |
| retval.key_slot, file->size()); |
| return retval; // Empty secure_deletion_secret, key decryption will |
| // fail. |
| } |
| |
| std::optional<Buffer> secret = |
| file->ReadBlock(retval.key_slot * kSecretSize, kSecretSize); |
| if (!secret) { |
| LOG_E("Failed to read secret from slot %u", retval.key_slot); |
| continue; |
| } |
| |
| LOG_D("Read secure deletion secret, size: %zu", |
| secret->available_read()); |
| retval.secure_deletion_secret = std::move(*secret); |
| break; |
| } |
| |
| return retval; |
| } |
| |
| void TrustySecureDeletionSecretStorage::DeleteKey(uint32_t key_slot) const { |
| if (key_slot == 0) { |
| LOG_D("key_slot == 0, nothing to delete", 0); |
| return; |
| } |
| |
| for (;;) { |
| std::optional<StorageSession> session = |
| StorageSession::CreateSession(); // Will block |
| if (!session) { |
| LOG_E("Failed to open session to retrieve secure deletion data.", |
| 0); |
| continue; |
| } |
| |
| std::optional<StorageFile> file = |
| session->OpenFile(kSecureDeletionSecretFileName); |
| if (!file) { |
| LOG_E("Failed to open file to retrieve secure deletion data.", 0); |
| continue; |
| } |
| |
| storage_off_t key_slot_begin = key_slot * kSecretSize; |
| storage_off_t key_slot_end = key_slot_begin + kSecretSize; |
| if (key_slot_begin < |
| kFactoryResetSecretPos + kFactoryResetSecretSize // |
| || key_slot_end > file->size()) { |
| LOG_E("Attempted to delete invalid key slot %u", key_slot); |
| return; |
| } |
| |
| if (!zero_entries(*file, key_slot_begin, key_slot_end)) { |
| continue; |
| } |
| LOG_D("Deleted secure key slot %u, zeroing %llu to %llu", key_slot, |
| key_slot_begin, key_slot_end); |
| |
| if (!session->EndTransaction(true /* commit */)) { |
| LOG_E("Failed to commit transaction deleting key at slot %u", |
| key_slot); |
| continue; |
| } |
| LOG_D("Committed deletion", 0); |
| |
| return; |
| } |
| } |
| |
| void TrustySecureDeletionSecretStorage::DeleteAllKeys() const { |
| for (;;) { |
| std::optional<StorageSession> session = |
| StorageSession::CreateSession(); // Will block |
| if (!session) { |
| LOG_E("Failed to open session to delete secrets file.", 0); |
| continue; |
| } |
| LOG_D("Opened session to delete secrets file.", 0); |
| |
| auto error = session->DeleteFile(kSecureDeletionSecretFileName); |
| if (error == StorageSession::Error::OK) { |
| LOG_D("Deleted secrets file", 0); |
| |
| if (!session->EndTransaction(true /* commit */)) { |
| LOG_E("Failed to commit deletion of secrets file.", 0); |
| } |
| LOG_D("Committed deletion of secrets file.", 0); |
| } else if (error == StorageSession::Error::NOT_FOUND) { |
| // File does not exist, may as well abandon the session. |
| LOG_D("No secrets file existed.", 0); |
| } else { |
| // Assuming transient error. Log and retry. |
| LOG_E("Failed to delete secrets file", 0); |
| continue; |
| } |
| |
| // Success |
| factory_reset_secret_ = {}; |
| return; |
| } |
| } |
| |
| } // namespace keymaster |