When we forget a volume, forget per-volume key
Protect all per-volume-per-user keys with a per-volume key, which is forgotten when the volume is forgotten. This means that the user's key is securely lost even when their storage is encrypted at forgetting time. Bug: 25861755 Test: create a volume, forget it, check logs and filesystem. Change-Id: I8df77bc91bbfa2258e082ddd54d6160dbf39b378
This commit is contained in:
parent
c6433a299d
commit
26a53888a4
5 changed files with 77 additions and 34 deletions
|
@ -79,6 +79,9 @@ const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/us
|
||||||
const std::string user_key_temp = user_key_dir + "/temp";
|
const std::string user_key_temp = user_key_dir + "/temp";
|
||||||
const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
|
const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
|
||||||
|
|
||||||
|
const std::string systemwide_volume_key_dir =
|
||||||
|
std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
|
||||||
|
|
||||||
bool s_global_de_initialized = false;
|
bool s_global_de_initialized = false;
|
||||||
|
|
||||||
// Some users are ephemeral, don't try to wipe their keys from disk
|
// Some users are ephemeral, don't try to wipe their keys from disk
|
||||||
|
@ -336,8 +339,8 @@ bool e4crypt_initialize_global_de() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PolicyKeyRef device_ref;
|
PolicyKeyRef device_ref;
|
||||||
if (!android::vold::retrieveAndInstallKey(true, device_key_path, device_key_temp,
|
if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
|
||||||
&device_ref.key_raw_ref))
|
device_key_temp, &device_ref.key_raw_ref))
|
||||||
return false;
|
return false;
|
||||||
get_data_file_encryption_modes(&device_ref);
|
get_data_file_encryption_modes(&device_ref);
|
||||||
|
|
||||||
|
@ -503,14 +506,32 @@ static std::string volkey_path(const std::string& misc_path, const std::string&
|
||||||
return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
|
return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string volume_secdiscardable_path(const std::string& volume_uuid) {
|
||||||
|
return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable";
|
||||||
|
}
|
||||||
|
|
||||||
static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
|
static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
|
||||||
PolicyKeyRef* key_ref) {
|
PolicyKeyRef* key_ref) {
|
||||||
|
auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
|
||||||
|
std::string secdiscardable_hash;
|
||||||
|
if (android::vold::pathExists(secdiscardable_path)) {
|
||||||
|
if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
|
||||||
|
PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
auto key_path = volkey_path(misc_path, volume_uuid);
|
auto key_path = volkey_path(misc_path, volume_uuid);
|
||||||
if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
|
if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
|
||||||
PLOG(ERROR) << "Creating directories for: " << key_path;
|
PLOG(ERROR) << "Creating directories for: " << key_path;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!android::vold::retrieveAndInstallKey(true, key_path, key_path + "_tmp",
|
android::vold::KeyAuthentication auth("", secdiscardable_hash);
|
||||||
|
if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp",
|
||||||
&key_ref->key_raw_ref))
|
&key_ref->key_raw_ref))
|
||||||
return false;
|
return false;
|
||||||
key_ref->contents_mode =
|
key_ref->contents_mode =
|
||||||
|
@ -798,6 +819,8 @@ static bool destroy_volume_keys(const std::string& directory_path, const std::st
|
||||||
bool e4crypt_destroy_volume_keys(const std::string& volume_uuid) {
|
bool e4crypt_destroy_volume_keys(const std::string& volume_uuid) {
|
||||||
bool res = true;
|
bool res = true;
|
||||||
LOG(DEBUG) << "e4crypt_destroy_volume_keys for volume " << escape_empty(volume_uuid);
|
LOG(DEBUG) << "e4crypt_destroy_volume_keys for volume " << escape_empty(volume_uuid);
|
||||||
|
auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
|
||||||
|
res &= android::vold::runSecdiscardSingle(secdiscardable_path);
|
||||||
res &= destroy_volume_keys("/data/misc_ce", volume_uuid);
|
res &= destroy_volume_keys("/data/misc_ce", volume_uuid);
|
||||||
res &= destroy_volume_keys("/data/misc_de", volume_uuid);
|
res &= destroy_volume_keys("/data/misc_de", volume_uuid);
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -88,7 +88,7 @@ static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string hashWithPrefix(char const* prefix, const std::string& tohash) {
|
static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
|
||||||
SHA512_CTX c;
|
SHA512_CTX c;
|
||||||
|
|
||||||
SHA512_Init(&c);
|
SHA512_Init(&c);
|
||||||
|
@ -99,9 +99,8 @@ static std::string hashWithPrefix(char const* prefix, const std::string& tohash)
|
||||||
hashingPrefix.resize(SHA512_CBLOCK);
|
hashingPrefix.resize(SHA512_CBLOCK);
|
||||||
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
|
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
|
||||||
SHA512_Update(&c, tohash.data(), tohash.size());
|
SHA512_Update(&c, tohash.data(), tohash.size());
|
||||||
std::string res(SHA512_DIGEST_LENGTH, '\0');
|
res->assign(SHA512_DIGEST_LENGTH, '\0');
|
||||||
SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
|
SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
|
static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
|
||||||
|
@ -160,6 +159,30 @@ static bool writeStringToFile(const std::string& payload, const std::string& fil
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool readRandomBytesOrLog(size_t count, std::string* out) {
|
||||||
|
auto status = ReadRandomBytes(count, *out);
|
||||||
|
if (status != OK) {
|
||||||
|
LOG(ERROR) << "Random read failed with status: " << status;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createSecdiscardable(const std::string& filename, std::string* hash) {
|
||||||
|
std::string secdiscardable;
|
||||||
|
if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
|
||||||
|
if (!writeStringToFile(secdiscardable, filename)) return false;
|
||||||
|
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readSecdiscardable(const std::string& filename, std::string* hash) {
|
||||||
|
std::string secdiscardable;
|
||||||
|
if (!readFileToString(filename, &secdiscardable)) return false;
|
||||||
|
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
||||||
KeyPurpose purpose,
|
KeyPurpose purpose,
|
||||||
const AuthorizationSet &keyParams,
|
const AuthorizationSet &keyParams,
|
||||||
|
@ -283,20 +306,11 @@ static bool stretchSecret(const std::string& stretching, const std::string& secr
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
|
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
|
||||||
const std::string& salt, const std::string& secdiscardable,
|
const std::string& salt, const std::string& secdiscardable_hash,
|
||||||
std::string* appId) {
|
std::string* appId) {
|
||||||
std::string stretched;
|
std::string stretched;
|
||||||
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
|
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
|
||||||
*appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
|
*appId = secdiscardable_hash + stretched;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool readRandomBytesOrLog(size_t count, std::string* out) {
|
|
||||||
auto status = ReadRandomBytes(count, *out);
|
|
||||||
if (status != OK) {
|
|
||||||
LOG(ERROR) << "Random read failed with status: " << status;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +320,8 @@ static void logOpensslError() {
|
||||||
|
|
||||||
static bool encryptWithoutKeymaster(const std::string& preKey,
|
static bool encryptWithoutKeymaster(const std::string& preKey,
|
||||||
const KeyBuffer& plaintext, std::string* ciphertext) {
|
const KeyBuffer& plaintext, std::string* ciphertext) {
|
||||||
auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
|
std::string key;
|
||||||
|
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
|
||||||
key.resize(AES_KEY_BYTES);
|
key.resize(AES_KEY_BYTES);
|
||||||
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
|
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
|
||||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||||
|
@ -356,7 +371,8 @@ static bool decryptWithoutKeymaster(const std::string& preKey,
|
||||||
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
|
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
|
std::string key;
|
||||||
|
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
|
||||||
key.resize(AES_KEY_BYTES);
|
key.resize(AES_KEY_BYTES);
|
||||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||||
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||||
|
@ -410,9 +426,8 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
|
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
|
||||||
std::string secdiscardable;
|
std::string secdiscardable_hash;
|
||||||
if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
|
if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
|
||||||
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
|
|
||||||
std::string stretching = getStretching(auth);
|
std::string stretching = getStretching(auth);
|
||||||
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
|
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
|
||||||
std::string salt;
|
std::string salt;
|
||||||
|
@ -424,7 +439,7 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu
|
||||||
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
|
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
|
||||||
}
|
}
|
||||||
std::string appId;
|
std::string appId;
|
||||||
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
|
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
|
||||||
std::string encryptedKey;
|
std::string encryptedKey;
|
||||||
if (auth.usesKeymaster()) {
|
if (auth.usesKeymaster()) {
|
||||||
Keymaster keymaster;
|
Keymaster keymaster;
|
||||||
|
@ -467,8 +482,8 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe
|
||||||
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
|
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string secdiscardable;
|
std::string secdiscardable_hash;
|
||||||
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
|
if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
|
||||||
std::string stretching;
|
std::string stretching;
|
||||||
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
|
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
|
||||||
std::string salt;
|
std::string salt;
|
||||||
|
@ -476,7 +491,7 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe
|
||||||
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
|
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
|
||||||
}
|
}
|
||||||
std::string appId;
|
std::string appId;
|
||||||
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
|
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
|
||||||
std::string encryptedMessage;
|
std::string encryptedMessage;
|
||||||
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
|
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
|
||||||
if (auth.usesKeymaster()) {
|
if (auth.usesKeymaster()) {
|
||||||
|
|
|
@ -44,6 +44,9 @@ extern const KeyAuthentication kEmptyAuthentication;
|
||||||
// Checks if path "path" exists.
|
// Checks if path "path" exists.
|
||||||
bool pathExists(const std::string& path);
|
bool pathExists(const std::string& path);
|
||||||
|
|
||||||
|
bool createSecdiscardable(const std::string& path, std::string* hash);
|
||||||
|
bool readSecdiscardable(const std::string& path, std::string* hash);
|
||||||
|
|
||||||
// Create a directory at the named path, and store "key" in it,
|
// Create a directory at the named path, and store "key" in it,
|
||||||
// in such a way that it can only be retrieved via Keymaster and
|
// in such a way that it can only be retrieved via Keymaster and
|
||||||
// can be securely deleted.
|
// can be securely deleted.
|
||||||
|
|
10
KeyUtil.cpp
10
KeyUtil.cpp
|
@ -161,12 +161,13 @@ bool evictKey(const std::string& raw_ref) {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path,
|
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
|
||||||
const std::string& tmp_path, std::string* key_ref) {
|
const std::string& key_path, const std::string& tmp_path,
|
||||||
|
std::string* key_ref) {
|
||||||
KeyBuffer key;
|
KeyBuffer key;
|
||||||
if (pathExists(key_path)) {
|
if (pathExists(key_path)) {
|
||||||
LOG(DEBUG) << "Key exists, using: " << key_path;
|
LOG(DEBUG) << "Key exists, using: " << key_path;
|
||||||
if (!retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
|
if (!retrieveKey(key_path, key_authentication, &key)) return false;
|
||||||
} else {
|
} else {
|
||||||
if (!create_if_absent) {
|
if (!create_if_absent) {
|
||||||
LOG(ERROR) << "No key found in " << key_path;
|
LOG(ERROR) << "No key found in " << key_path;
|
||||||
|
@ -174,8 +175,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path,
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Creating new key in " << key_path;
|
LOG(INFO) << "Creating new key in " << key_path;
|
||||||
if (!randomKey(&key)) return false;
|
if (!randomKey(&key)) return false;
|
||||||
if (!storeKeyAtomically(key_path, tmp_path,
|
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
|
||||||
kEmptyAuthentication, key)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!installKey(key, key_ref)) {
|
if (!installKey(key, key_ref)) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define ANDROID_VOLD_KEYUTIL_H
|
#define ANDROID_VOLD_KEYUTIL_H
|
||||||
|
|
||||||
#include "KeyBuffer.h"
|
#include "KeyBuffer.h"
|
||||||
|
#include "KeyStorage.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -28,8 +29,9 @@ namespace vold {
|
||||||
bool randomKey(KeyBuffer* key);
|
bool randomKey(KeyBuffer* key);
|
||||||
bool installKey(const KeyBuffer& key, std::string* raw_ref);
|
bool installKey(const KeyBuffer& key, std::string* raw_ref);
|
||||||
bool evictKey(const std::string& raw_ref);
|
bool evictKey(const std::string& raw_ref);
|
||||||
bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path,
|
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
|
||||||
const std::string& tmp_path, std::string* key_ref);
|
const std::string& key_path, const std::string& tmp_path,
|
||||||
|
std::string* key_ref);
|
||||||
bool retrieveKey(bool create_if_absent, const std::string& key_path,
|
bool retrieveKey(bool create_if_absent, const std::string& key_path,
|
||||||
const std::string& tmp_path, KeyBuffer* key);
|
const std::string& tmp_path, KeyBuffer* key);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue