Add support for binding storage encryption to a seed
With this change, vold exposes an API that may be used to bind key
storage encryption keys to a given seed value. The seed value passed to
vold must be consistent across reboots, or key storage keys will not be
derived consistently. The seed is expected to be set very early in boot,
prior to the use of any key storage encryption keys.
This feature is intended to be used for embedded applications such as
in autos, where the seed may be provided by some other component of the
system. In such systems, there is a default user that is automatically
signed in without a PIN or other credentials. By binding the file
encryption to a platform-provided seed, the default user's data gains
additional protection against removal of the Android embedded device
from the integrated system.
Bug: 157501579
Test: Set seed at startup via init.rc. Seed changes fail as expected.
Change-Id: I9b048ec5e045b84c45883724ace2356d4ef6244d
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index 8147827..89844aa 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -22,6 +22,8 @@
#include "Utils.h"
#include <algorithm>
+#include <memory>
+#include <mutex>
#include <thread>
#include <vector>
@@ -82,6 +84,31 @@
static const char* kFn_stretching = "stretching";
static const char* kFn_version = "version";
+namespace {
+
+// Storage binding info for ensuring key encryption keys include a
+// platform-provided seed in their derivation.
+struct StorageBindingInfo {
+ enum class State {
+ UNINITIALIZED,
+ IN_USE, // key storage keys are bound to seed
+ NOT_USED, // key storage keys are NOT bound to seed
+ };
+
+ // Binding seed mixed into all key storage keys.
+ std::vector<uint8_t> seed;
+
+ // State tracker for the key storage key binding.
+ State state = State::UNINITIALIZED;
+
+ std::mutex guard;
+};
+
+// Never freed as the dtor is non-trivial.
+StorageBindingInfo& storage_binding_info = *new StorageBindingInfo;
+
+} // namespace
+
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
if (actual != expected) {
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
@@ -456,6 +483,20 @@
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
*appId = secdiscardable_hash + stretched;
+
+ const std::lock_guard<std::mutex> scope_lock(storage_binding_info.guard);
+ switch (storage_binding_info.state) {
+ case StorageBindingInfo::State::UNINITIALIZED:
+ storage_binding_info.state = StorageBindingInfo::State::NOT_USED;
+ break;
+ case StorageBindingInfo::State::IN_USE:
+ appId->append(storage_binding_info.seed.begin(), storage_binding_info.seed.end());
+ break;
+ case StorageBindingInfo::State::NOT_USED:
+ // noop
+ break;
+ }
+
return true;
}
@@ -715,5 +756,22 @@
return success;
}
+bool setKeyStorageBindingSeed(const std::vector<uint8_t>& seed) {
+ const std::lock_guard<std::mutex> scope_lock(storage_binding_info.guard);
+ switch (storage_binding_info.state) {
+ case StorageBindingInfo::State::UNINITIALIZED:
+ storage_binding_info.state = StorageBindingInfo::State::IN_USE;
+ storage_binding_info.seed = seed;
+ return true;
+ case StorageBindingInfo::State::IN_USE:
+ LOG(ERROR) << "key storage binding seed already set";
+ return false;
+ case StorageBindingInfo::State::NOT_USED:
+ LOG(ERROR) << "key storage already in use without binding";
+ return false;
+ }
+ return false;
+}
+
} // namespace vold
} // namespace android