Add Support for metadata key with rollback
This adds the ability to upgrade a key and retain the old one for rollback purposes. We delete the old key if we boot successfully and delete the new key if we do not. Test: Enable checkpointing and test rolling back between two versions Bug: 111020314 Change-Id: I19f31a1ac06a811c0644fc956e61b5ca84e7241a
This commit is contained in:
parent
ffa1bb0370
commit
690d6de5bf
5 changed files with 83 additions and 22 deletions
|
@ -202,7 +202,7 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
||||||
km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
|
km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
|
||||||
const km::AuthorizationSet& opParams,
|
const km::AuthorizationSet& opParams,
|
||||||
const km::HardwareAuthToken& authToken,
|
const km::HardwareAuthToken& authToken,
|
||||||
km::AuthorizationSet* outParams) {
|
km::AuthorizationSet* outParams, bool keepOld) {
|
||||||
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
|
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
|
||||||
std::string kmKey;
|
std::string kmKey;
|
||||||
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
|
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
|
||||||
|
@ -219,12 +219,14 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
||||||
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
|
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
|
||||||
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
|
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
|
||||||
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
|
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
|
||||||
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
|
if (!keepOld) {
|
||||||
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
|
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
|
||||||
return KeymasterOperation();
|
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
|
||||||
}
|
return KeymasterOperation();
|
||||||
if (!keymaster.deleteKey(kmKey)) {
|
}
|
||||||
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
|
if (!keymaster.deleteKey(kmKey)) {
|
||||||
|
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kmKey = newKey;
|
kmKey = newKey;
|
||||||
LOG(INFO) << "Key upgraded: " << dir;
|
LOG(INFO) << "Key upgraded: " << dir;
|
||||||
|
@ -233,12 +235,12 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
||||||
|
|
||||||
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
||||||
const km::AuthorizationSet& keyParams,
|
const km::AuthorizationSet& keyParams,
|
||||||
const km::HardwareAuthToken& authToken,
|
const km::HardwareAuthToken& authToken, const KeyBuffer& message,
|
||||||
const KeyBuffer& message, std::string* ciphertext) {
|
std::string* ciphertext, bool keepOld) {
|
||||||
km::AuthorizationSet opParams;
|
km::AuthorizationSet opParams;
|
||||||
km::AuthorizationSet outParams;
|
km::AuthorizationSet outParams;
|
||||||
auto opHandle =
|
auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
|
||||||
begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams);
|
&outParams, keepOld);
|
||||||
if (!opHandle) return false;
|
if (!opHandle) return false;
|
||||||
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
|
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
|
||||||
if (!nonceBlob.isOk()) {
|
if (!nonceBlob.isOk()) {
|
||||||
|
@ -262,13 +264,14 @@ static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir
|
||||||
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
||||||
const km::AuthorizationSet& keyParams,
|
const km::AuthorizationSet& keyParams,
|
||||||
const km::HardwareAuthToken& authToken,
|
const km::HardwareAuthToken& authToken,
|
||||||
const std::string& ciphertext, KeyBuffer* message) {
|
const std::string& ciphertext, KeyBuffer* message,
|
||||||
|
bool keepOld) {
|
||||||
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
|
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
|
||||||
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
|
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
|
||||||
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
|
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
|
||||||
km::support::blob2hidlVec(nonce));
|
km::support::blob2hidlVec(nonce));
|
||||||
auto opHandle =
|
auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
|
||||||
begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr);
|
nullptr, keepOld);
|
||||||
if (!opHandle) return false;
|
if (!opHandle) return false;
|
||||||
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
|
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
|
||||||
if (!opHandle.finish(nullptr)) return false;
|
if (!opHandle.finish(nullptr)) return false;
|
||||||
|
@ -474,7 +477,8 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu
|
||||||
km::AuthorizationSet keyParams;
|
km::AuthorizationSet keyParams;
|
||||||
km::HardwareAuthToken authToken;
|
km::HardwareAuthToken authToken;
|
||||||
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
||||||
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
|
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
|
||||||
|
false))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
|
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
|
||||||
|
@ -502,7 +506,8 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
|
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
|
||||||
|
bool keepOld) {
|
||||||
std::string version;
|
std::string version;
|
||||||
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
|
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
|
||||||
if (version != kCurrentVersion) {
|
if (version != kCurrentVersion) {
|
||||||
|
@ -527,7 +532,8 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe
|
||||||
km::AuthorizationSet keyParams;
|
km::AuthorizationSet keyParams;
|
||||||
km::HardwareAuthToken authToken;
|
km::HardwareAuthToken authToken;
|
||||||
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
||||||
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
|
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
|
||||||
|
keepOld))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
|
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
|
||||||
|
|
|
@ -61,7 +61,8 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
|
||||||
const KeyAuthentication& auth, const KeyBuffer& key);
|
const KeyAuthentication& auth, const KeyBuffer& key);
|
||||||
|
|
||||||
// Retrieve the key from the named directory.
|
// Retrieve the key from the named directory.
|
||||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
|
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
|
||||||
|
bool keepOld = false);
|
||||||
|
|
||||||
// Securely destroy the key stored in the named directory and delete the directory.
|
// Securely destroy the key stored in the named directory and delete the directory.
|
||||||
bool destroyKey(const std::string& dir);
|
bool destroyKey(const std::string& dir);
|
||||||
|
|
|
@ -169,10 +169,10 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
||||||
KeyBuffer* key) {
|
KeyBuffer* key, bool keepOld) {
|
||||||
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, kEmptyAuthentication, key, keepOld)) 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;
|
||||||
|
|
|
@ -33,7 +33,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
|
||||||
const std::string& key_path, const std::string& tmp_path,
|
const std::string& key_path, const std::string& tmp_path,
|
||||||
std::string* key_ref);
|
std::string* key_ref);
|
||||||
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
||||||
KeyBuffer* key);
|
KeyBuffer* key, bool keepOld = true);
|
||||||
|
|
||||||
} // namespace vold
|
} // namespace vold
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <linux/dm-ioctl.h>
|
#include <linux/dm-ioctl.h>
|
||||||
|
|
||||||
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/properties.h>
|
#include <android-base/properties.h>
|
||||||
#include <android-base/unique_fd.h>
|
#include <android-base/unique_fd.h>
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
#include "EncryptInplace.h"
|
#include "EncryptInplace.h"
|
||||||
#include "KeyStorage.h"
|
#include "KeyStorage.h"
|
||||||
#include "KeyUtil.h"
|
#include "KeyUtil.h"
|
||||||
|
#include "Keymaster.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "VoldUtil.h"
|
#include "VoldUtil.h"
|
||||||
#include "secontext.h"
|
#include "secontext.h"
|
||||||
|
@ -52,6 +54,9 @@ using android::vold::KeyBuffer;
|
||||||
|
|
||||||
static const std::string kDmNameUserdata = "userdata";
|
static const std::string kDmNameUserdata = "userdata";
|
||||||
|
|
||||||
|
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
|
||||||
|
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
|
||||||
|
|
||||||
static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
|
static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
|
||||||
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
|
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
|
||||||
// partitions in the fsck domain.
|
// partitions in the fsck domain.
|
||||||
|
@ -74,12 +79,41 @@ static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace vold {
|
||||||
|
|
||||||
|
// Note: It is possible to orphan a key if it is removed before deleting
|
||||||
|
// Update this once keymaster APIs change, and we have a proper commit.
|
||||||
|
static void commit_key(std::string dir) {
|
||||||
|
while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
|
||||||
|
LOG(ERROR) << "Wait for boot timed out";
|
||||||
|
}
|
||||||
|
Keymaster keymaster;
|
||||||
|
auto keyPath = dir + "/" + kFn_keymaster_key_blob;
|
||||||
|
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
|
||||||
|
std::string key;
|
||||||
|
|
||||||
|
if (!android::base::ReadFileToString(keyPath, &key)) {
|
||||||
|
LOG(ERROR) << "Failed to read old key: " << dir;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
|
||||||
|
PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!keymaster.deleteKey(key)) {
|
||||||
|
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Old Key deleted: " << dir;
|
||||||
|
}
|
||||||
|
|
||||||
static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, KeyBuffer* key) {
|
static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, KeyBuffer* key) {
|
||||||
if (!data_rec->key_dir) {
|
if (!data_rec->key_dir) {
|
||||||
LOG(ERROR) << "Failed to get key_dir";
|
LOG(ERROR) << "Failed to get key_dir";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string key_dir = data_rec->key_dir;
|
std::string key_dir = data_rec->key_dir;
|
||||||
|
std::string sKey;
|
||||||
auto dir = key_dir + "/key";
|
auto dir = key_dir + "/key";
|
||||||
LOG(DEBUG) << "key_dir/key: " << dir;
|
LOG(DEBUG) << "key_dir/key: " << dir;
|
||||||
if (fs_mkdirs(dir.c_str(), 0700)) {
|
if (fs_mkdirs(dir.c_str(), 0700)) {
|
||||||
|
@ -87,10 +121,30 @@ static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, Ke
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto temp = key_dir + "/tmp";
|
auto temp = key_dir + "/tmp";
|
||||||
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
|
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
|
||||||
|
/* If we have a leftover upgraded key, delete it.
|
||||||
|
* We either failed an update and must return to the old key,
|
||||||
|
* or we rebooted before commiting the keys in a freak accident.
|
||||||
|
* Either way, we can re-upgrade the key if we need to.
|
||||||
|
*/
|
||||||
|
Keymaster keymaster;
|
||||||
|
if (pathExists(newKeyPath)) {
|
||||||
|
if (!android::base::ReadFileToString(newKeyPath, &sKey))
|
||||||
|
LOG(ERROR) << "Failed to read old key: " << dir;
|
||||||
|
else if (!keymaster.deleteKey(sKey))
|
||||||
|
LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir;
|
||||||
|
else
|
||||||
|
unlink(newKeyPath.c_str());
|
||||||
|
}
|
||||||
|
bool needs_cp = cp_needsCheckpoint();
|
||||||
|
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key, needs_cp)) return false;
|
||||||
|
if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace vold
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
|
static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
|
||||||
KeyBuffer hex_key;
|
KeyBuffer hex_key;
|
||||||
if (android::vold::StrToHex(key, hex_key) != android::OK) {
|
if (android::vold::StrToHex(key, hex_key) != android::OK) {
|
||||||
|
|
Loading…
Reference in a new issue