Skip to content

Commit

Permalink
Zero memory used for encryuption keys.
Browse files Browse the repository at this point in the history
std::vector with custom zeroing allocator is used instead of
std::string for data that can contain encryption keys.

Bug: 64201177
Test: manually created a managed profile, changed it's credentials
Test: manually upgraded a phone with profile from O to MR1.
Change-Id: Ic31877049f69eba9f8ea64fd99acaaca5a01d3dd
  • Loading branch information
Pavel Grafov committed Aug 10, 2017
1 parent 53deec1 commit e2e2d30
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 75 deletions.
1 change: 1 addition & 0 deletions Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ common_src_files := \
MoveTask.cpp \
Benchmark.cpp \
TrimTask.cpp \
KeyBuffer.cpp \
Keymaster.cpp \
KeyStorage.cpp \
KeyUtil.cpp \
Expand Down
17 changes: 9 additions & 8 deletions Ext4Crypt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::vold::kEmptyAuthentication;
using android::vold::KeyBuffer;

// NOTE: keep in sync with StorageManager
static constexpr int FLAG_STORAGE_DE = 1 << 0;
Expand All @@ -80,7 +81,7 @@ std::set<userid_t> s_ephemeral_users;
std::map<userid_t, std::string> s_de_key_raw_refs;
std::map<userid_t, std::string> s_ce_key_raw_refs;
// TODO abolish this map, per b/26948053
std::map<userid_t, std::string> s_ce_keys;
std::map<userid_t, KeyBuffer> s_ce_keys;

}

Expand Down Expand Up @@ -170,7 +171,7 @@ static void fixate_user_ce_key(const std::string& directory_path, const std::str

static bool read_and_fixate_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth,
std::string *ce_key) {
KeyBuffer *ce_key) {
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path);
for (auto const ce_key_path: paths) {
Expand All @@ -188,11 +189,11 @@ static bool read_and_fixate_user_ce_key(userid_t user_id,
static bool read_and_install_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth) {
if (s_ce_key_raw_refs.count(user_id) != 0) return true;
std::string ce_key;
KeyBuffer ce_key;
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
std::string ce_raw_ref;
if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_keys[user_id] = std::move(ce_key);
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Installed ce key for user " << user_id;
return true;
Expand All @@ -219,7 +220,7 @@ static bool destroy_dir(const std::string& dir) {
// NB this assumes that there is only one thread listening for crypt commands, because
// it creates keys in a fixed location.
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
std::string de_key, ce_key;
KeyBuffer de_key, ce_key;
if (!android::vold::randomKey(&de_key)) return false;
if (!android::vold::randomKey(&ce_key)) return false;
if (create_ephemeral) {
Expand Down Expand Up @@ -306,7 +307,7 @@ static bool load_all_de_keys() {
userid_t user_id = atoi(entry->d_name);
if (s_de_key_raw_refs.count(user_id) == 0) {
auto key_path = de_dir + "/" + entry->d_name;
std::string key;
KeyBuffer key;
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
std::string raw_ref;
if (!android::vold::installKey(key, &raw_ref)) return false;
Expand Down Expand Up @@ -411,7 +412,7 @@ static void drop_caches() {
}

static bool evict_ce_key(userid_t user_id) {
s_ce_keys.erase(user_id);
s_ce_keys.erase(user_id);
bool success = true;
std::string raw_ref;
// If we haven't loaded the CE key, no need to evict it.
Expand Down Expand Up @@ -509,7 +510,7 @@ bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token_h
LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id;
return false;
}
auto ce_key = it->second;
const auto &ce_key = it->second;
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path;
Expand Down
37 changes: 37 additions & 0 deletions KeyBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 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 "KeyBuffer.h"

#include <algorithm>
#include <cstring>

namespace android {
namespace vold {

KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
return std::move(lhs);
}

KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
return std::move(lhs);
}

} // namespace vold
} // namespace android

63 changes: 63 additions & 0 deletions KeyBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2017 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.
*/

#ifndef ANDROID_VOLD_KEYBUFFER_H
#define ANDROID_VOLD_KEYBUFFER_H

#include <cstring>
#include <memory>
#include <vector>

namespace android {
namespace vold {

/**
* Variant of memset() that should never be optimized away. Borrowed from keymaster code.
*/
#ifdef __clang__
#define OPTNONE __attribute__((optnone))
#else // not __clang__
#define OPTNONE __attribute__((optimize("O0")))
#endif // not __clang__
inline OPTNONE void* memset_s(void* s, int c, size_t n) {
if (!s)
return s;
return memset(s, c, n);
}
#undef OPTNONE

// Allocator that delegates useful work to standard one but zeroes data before deallocating.
class ZeroingAllocator : public std::allocator<char> {
public:
void deallocate(pointer p, size_type n)
{
memset_s(p, 0, n);
std::allocator<char>::deallocate(p, n);
}
};

// Char vector that zeroes memory when deallocating.
using KeyBuffer = std::vector<char, ZeroingAllocator>;

// Convenience methods to concatenate key buffers.
KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);

} // namespace vold
} // namespace android

#endif

16 changes: 8 additions & 8 deletions KeyStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,

static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const AuthorizationSet &keyParams,
const std::string& message, std::string* ciphertext) {
const KeyBuffer& message, std::string* ciphertext) {
AuthorizationSet opParams;
AuthorizationSet outParams;
auto opHandle = begin(keymaster, dir, KeyPurpose::ENCRYPT, keyParams, opParams, &outParams);
Expand All @@ -220,7 +220,7 @@ static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir

static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const AuthorizationSet &keyParams,
const std::string& ciphertext, std::string* message) {
const std::string& ciphertext, KeyBuffer* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto opParams = AuthorizationSetBuilder()
Expand Down Expand Up @@ -305,7 +305,7 @@ static void logOpensslError() {
}

static bool encryptWithoutKeymaster(const std::string& preKey,
const std::string& plaintext, std::string* ciphertext) {
const KeyBuffer& plaintext, std::string* ciphertext) {
auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
key.resize(AES_KEY_BYTES);
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
Expand Down Expand Up @@ -351,7 +351,7 @@ static bool encryptWithoutKeymaster(const std::string& preKey,
}

static bool decryptWithoutKeymaster(const std::string& preKey,
const std::string& ciphertext, std::string* plaintext) {
const std::string& ciphertext, KeyBuffer* plaintext) {
if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
return false;
Expand All @@ -370,7 +370,7 @@ static bool decryptWithoutKeymaster(const std::string& preKey,
logOpensslError();
return false;
}
plaintext->resize(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
*plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
int outlen;
if (1 != EVP_DecryptUpdate(ctx.get(),
reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
Expand Down Expand Up @@ -404,7 +404,7 @@ bool pathExists(const std::string& path) {
return access(path.c_str(), F_OK) == 0;
}

bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir;
return false;
Expand Down Expand Up @@ -442,7 +442,7 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::
}

bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& auth, const std::string& key) {
const KeyAuthentication& auth, const KeyBuffer& key) {
if (pathExists(key_path)) {
LOG(ERROR) << "Already exists, cannot create key at: " << key_path;
return false;
Expand All @@ -460,7 +460,7 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
return true;
}

bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
Expand Down
8 changes: 5 additions & 3 deletions KeyStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef ANDROID_VOLD_KEYSTORAGE_H
#define ANDROID_VOLD_KEYSTORAGE_H

#include "KeyBuffer.h"

#include <string>

namespace android {
Expand Down Expand Up @@ -46,17 +48,17 @@ bool pathExists(const std::string& path);
// in such a way that it can only be retrieved via Keymaster and
// can be securely deleted.
// It's safe to move/rename the directory after creation.
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);

// Create a directory at the named path, and store "key" in it as storeKey
// This version creates the key in "tmp_path" then atomically renames "tmp_path"
// to "key_path" thereby ensuring that the key is either stored entirely or
// not at all.
bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& auth, const std::string& key);
const KeyAuthentication& auth, const KeyBuffer& key);

// Retrieve the key from the named directory.
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);

// Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir);
Expand Down
32 changes: 25 additions & 7 deletions KeyUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,23 @@
namespace android {
namespace vold {

bool randomKey(std::string* key) {
if (ReadRandomBytes(EXT4_AES_256_XTS_KEY_SIZE, *key) != 0) {
// ext4enc:TODO get this const from somewhere good
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;

// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
constexpr int EXT4_MAX_KEY_SIZE = 64;
struct ext4_encryption_key {
uint32_t mode;
char raw[EXT4_MAX_KEY_SIZE];
uint32_t size;
};

bool randomKey(KeyBuffer* key) {
*key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
if (ReadRandomBytes(key->size(), key->data()) != 0) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
Expand All @@ -60,7 +75,7 @@ static std::string generateKeyRef(const char* key, int length) {
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
}

static bool fillKey(const std::string& key, ext4_encryption_key* ext4_key) {
static bool fillKey(const KeyBuffer& key, ext4_encryption_key* ext4_key) {
if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
Expand Down Expand Up @@ -101,8 +116,11 @@ static bool e4cryptKeyring(key_serial_t* device_keyring) {

// Install password into global keyring
// Return raw key reference for use in policy
bool installKey(const std::string& key, std::string* raw_ref) {
ext4_encryption_key ext4_key;
bool installKey(const KeyBuffer& key, std::string* raw_ref) {
// Place ext4_encryption_key into automatically zeroing buffer.
KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key));
ext4_encryption_key &ext4_key = *reinterpret_cast<ext4_encryption_key*>(ext4KeyBuffer.data());

if (!fillKey(key, &ext4_key)) return false;
*raw_ref = generateKeyRef(ext4_key.raw, ext4_key.size);
key_serial_t device_keyring;
Expand Down Expand Up @@ -145,7 +163,7 @@ bool evictKey(const std::string& raw_ref) {

bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path,
const std::string& tmp_path, std::string* key_ref) {
std::string key;
KeyBuffer key;
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
Expand All @@ -168,7 +186,7 @@ bool retrieveAndInstallKey(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, std::string* key) {
const std::string& tmp_path, KeyBuffer* key) {
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false;
Expand Down
23 changes: 6 additions & 17 deletions KeyUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,21 @@
#ifndef ANDROID_VOLD_KEYUTIL_H
#define ANDROID_VOLD_KEYUTIL_H

#include "KeyBuffer.h"

#include <string>
#include <memory>

namespace android {
namespace vold {

// ext4enc:TODO get this const from somewhere good
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;

// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
constexpr int EXT4_MAX_KEY_SIZE = 64;
struct ext4_encryption_key {
uint32_t mode;
char raw[EXT4_MAX_KEY_SIZE];
uint32_t size;
};

bool randomKey(std::string* key);
bool installKey(const std::string& key, std::string* raw_ref);
bool randomKey(KeyBuffer* key);
bool installKey(const KeyBuffer& key, std::string* raw_ref);
bool evictKey(const std::string& raw_ref);
bool retrieveAndInstallKey(bool create_if_absent, 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,
const std::string& tmp_path, std::string* key);
const std::string& tmp_path, KeyBuffer* key);

} // namespace vold
} // namespace android
Expand Down
Loading

0 comments on commit e2e2d30

Please sign in to comment.