Securely encrypt the master key
Move all key management into vold Reuse vold's existing key management through the crypto footer to manage the device wide keys. Use ro.crypto.type flag to determine crypto type, which prevents any issues when running in block encrypted mode, as well as speeding up boot in block or no encryption. This is one of four changes to enable this functionality: https://android-review.googlesource.com/#/c/148586/ https://android-review.googlesource.com/#/c/148604/ https://android-review.googlesource.com/#/c/148606/ https://android-review.googlesource.com/#/c/148607/ Bug: 18151196 Change-Id: I3c68691717a61b5e1df76423ca0c02baff0dab98
This commit is contained in:
parent
beadcb6ec0
commit
707fd6c7cc
6 changed files with 458 additions and 4 deletions
|
@ -15,6 +15,7 @@ common_src_files := \
|
|||
Devmapper.cpp \
|
||||
ResponseCode.cpp \
|
||||
CheckBattery.cpp \
|
||||
Ext4Crypt.cpp \
|
||||
VoldUtil.c \
|
||||
fstrim.c \
|
||||
cryptfs.c
|
||||
|
|
|
@ -619,6 +619,14 @@ int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
|
|||
Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(argv[1], "enablefilecrypto")) {
|
||||
const char* syntax = "Usage: cryptfs enablefilecrypto";
|
||||
if (argc != 2) {
|
||||
cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
|
||||
return 0;
|
||||
}
|
||||
dumpArgs(argc, argv, -1);
|
||||
rc = cryptfs_enable_file();
|
||||
} else if (!strcmp(argv[1], "changepw")) {
|
||||
const char* syntax = "Usage: cryptfs changepw "
|
||||
"default|password|pin|pattern [newpasswd]";
|
||||
|
|
377
Ext4Crypt.cpp
Normal file
377
Ext4Crypt.cpp
Normal file
|
@ -0,0 +1,377 @@
|
|||
#include "Ext4Crypt.h"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include "unencrypted_properties.h"
|
||||
#include "key_control.h"
|
||||
#include "cryptfs.h"
|
||||
|
||||
#define LOG_TAG "Ext4Crypt"
|
||||
#include "cutils/log.h"
|
||||
#include <cutils/klog.h>
|
||||
|
||||
namespace {
|
||||
// Key length in bits
|
||||
const int key_length = 128;
|
||||
|
||||
// How is device encrypted
|
||||
struct keys {
|
||||
std::string master_key;
|
||||
std::string password;
|
||||
};
|
||||
std::map<std::string, keys> s_key_store;
|
||||
|
||||
// ext4enc:TODO Include structure from somewhere sensible
|
||||
// MUST be in sync with ext4_crypto.c in kernel
|
||||
const int EXT4_MAX_KEY_SIZE = 76;
|
||||
struct ext4_encryption_key {
|
||||
uint32_t mode;
|
||||
char raw[EXT4_MAX_KEY_SIZE];
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
namespace tag {
|
||||
const char* magic = "magic";
|
||||
const char* major_version = "major_version";
|
||||
const char* minor_version = "minor_version";
|
||||
const char* flags = "flags";
|
||||
const char* crypt_type = "crypt_type";
|
||||
const char* failed_decrypt_count = "failed_decrypt_count";
|
||||
const char* crypto_type_name = "crypto_type_name";
|
||||
const char* master_key = "master_key";
|
||||
const char* salt = "salt";
|
||||
const char* kdf_type = "kdf_type";
|
||||
const char* N_factor = "N_factor";
|
||||
const char* r_factor = "r_factor";
|
||||
const char* p_factor = "p_factor";
|
||||
const char* keymaster_blob = "keymaster_blob";
|
||||
const char* scrypted_intermediate_key = "scrypted_intermediate_key";
|
||||
}
|
||||
}
|
||||
|
||||
static int put_crypt_ftr_and_key(const crypt_mnt_ftr& crypt_ftr,
|
||||
UnencryptedProperties& props)
|
||||
{
|
||||
SLOGI("Putting crypt footer");
|
||||
|
||||
bool success = props.Set<int>(tag::magic, crypt_ftr.magic)
|
||||
&& props.Set<int>(tag::major_version, crypt_ftr.major_version)
|
||||
&& props.Set<int>(tag::minor_version, crypt_ftr.minor_version)
|
||||
&& props.Set<int>(tag::flags, crypt_ftr.flags)
|
||||
&& props.Set<int>(tag::crypt_type, crypt_ftr.crypt_type)
|
||||
&& props.Set<int>(tag::failed_decrypt_count,
|
||||
crypt_ftr.failed_decrypt_count)
|
||||
&& props.Set<std::string>(tag::crypto_type_name,
|
||||
std::string(reinterpret_cast<const char*>(crypt_ftr.crypto_type_name)))
|
||||
&& props.Set<std::string>(tag::master_key,
|
||||
std::string((const char*) crypt_ftr.master_key,
|
||||
crypt_ftr.keysize))
|
||||
&& props.Set<std::string>(tag::salt,
|
||||
std::string((const char*) crypt_ftr.salt,
|
||||
SALT_LEN))
|
||||
&& props.Set<int>(tag::kdf_type, crypt_ftr.kdf_type)
|
||||
&& props.Set<int>(tag::N_factor, crypt_ftr.N_factor)
|
||||
&& props.Set<int>(tag::r_factor, crypt_ftr.r_factor)
|
||||
&& props.Set<int>(tag::p_factor, crypt_ftr.p_factor)
|
||||
&& props.Set<std::string>(tag::keymaster_blob,
|
||||
std::string((const char*) crypt_ftr.keymaster_blob,
|
||||
crypt_ftr.keymaster_blob_size))
|
||||
&& props.Set<std::string>(tag::scrypted_intermediate_key,
|
||||
std::string((const char*) crypt_ftr.scrypted_intermediate_key,
|
||||
SCRYPT_LEN));
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
static int get_crypt_ftr_and_key(crypt_mnt_ftr& crypt_ftr,
|
||||
const UnencryptedProperties& props)
|
||||
{
|
||||
memset(&crypt_ftr, 0, sizeof(crypt_ftr));
|
||||
crypt_ftr.magic = props.Get<int>(tag::magic);
|
||||
crypt_ftr.major_version = props.Get<int>(tag::major_version);
|
||||
crypt_ftr.minor_version = props.Get<int>(tag::minor_version);
|
||||
crypt_ftr.flags = props.Get<int>(tag::flags);
|
||||
crypt_ftr.crypt_type = props.Get<int>(tag::crypt_type);
|
||||
crypt_ftr.failed_decrypt_count = props.Get<int>(tag::failed_decrypt_count);
|
||||
std::string crypto_type_name = props.Get<std::string>(tag::crypto_type_name);
|
||||
strlcpy(reinterpret_cast<char*>(crypt_ftr.crypto_type_name),
|
||||
crypto_type_name.c_str(),
|
||||
sizeof(crypt_ftr.crypto_type_name));
|
||||
std::string master_key = props.Get<std::string>(tag::master_key);
|
||||
crypt_ftr.keysize = master_key.size();
|
||||
if (crypt_ftr.keysize > sizeof(crypt_ftr.master_key)) {
|
||||
SLOGE("Master key size too long");
|
||||
return -1;
|
||||
}
|
||||
memcpy(crypt_ftr.master_key, &master_key[0], crypt_ftr.keysize);
|
||||
std::string salt = props.Get<std::string>(tag::salt);
|
||||
if (salt.size() != SALT_LEN) {
|
||||
SLOGE("Salt wrong length");
|
||||
return -1;
|
||||
}
|
||||
memcpy(crypt_ftr.salt, &salt[0], SALT_LEN);
|
||||
crypt_ftr.kdf_type = props.Get<int>(tag::kdf_type);
|
||||
crypt_ftr.N_factor = props.Get<int>(tag::N_factor);
|
||||
crypt_ftr.r_factor = props.Get<int>(tag::r_factor);
|
||||
crypt_ftr.p_factor = props.Get<int>(tag::p_factor);
|
||||
std::string keymaster_blob = props.Get<std::string>(tag::keymaster_blob);
|
||||
crypt_ftr.keymaster_blob_size = keymaster_blob.size();
|
||||
if (crypt_ftr.keymaster_blob_size > sizeof(crypt_ftr.keymaster_blob)) {
|
||||
SLOGE("Keymaster blob too long");
|
||||
return -1;
|
||||
}
|
||||
memcpy(crypt_ftr.keymaster_blob, &keymaster_blob[0],
|
||||
crypt_ftr.keymaster_blob_size);
|
||||
std::string scrypted_intermediate_key = props.Get<std::string>(tag::scrypted_intermediate_key);
|
||||
if (scrypted_intermediate_key.size() != SCRYPT_LEN) {
|
||||
SLOGE("scrypted intermediate key wrong length");
|
||||
return -1;
|
||||
}
|
||||
memcpy(crypt_ftr.scrypted_intermediate_key, &scrypted_intermediate_key[0],
|
||||
SCRYPT_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnencryptedProperties GetProps(const char* path)
|
||||
{
|
||||
return UnencryptedProperties(path);
|
||||
}
|
||||
|
||||
static UnencryptedProperties GetAltProps(const char* path)
|
||||
{
|
||||
return UnencryptedProperties((std::string() + path + "/tmp_mnt").c_str());
|
||||
}
|
||||
|
||||
static UnencryptedProperties GetPropsOrAltProps(const char* path)
|
||||
{
|
||||
UnencryptedProperties props = GetProps(path);
|
||||
if (props.OK()) {
|
||||
return props;
|
||||
}
|
||||
return GetAltProps(path);
|
||||
}
|
||||
|
||||
int e4crypt_enable(const char* path)
|
||||
{
|
||||
// Already enabled?
|
||||
if (s_key_store.find(path) != s_key_store.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not an encryptable device?
|
||||
UnencryptedProperties key_props = GetProps(path).GetChild(properties::key);
|
||||
if (!key_props.OK()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key_props.Get<std::string>(tag::master_key).empty()) {
|
||||
crypt_mnt_ftr ftr;
|
||||
if (cryptfs_create_default_ftr(&ftr, key_length)) {
|
||||
SLOGE("Failed to create crypto footer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (put_crypt_ftr_and_key(ftr, key_props)) {
|
||||
SLOGE("Failed to write crypto footer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
crypt_mnt_ftr ftr2;
|
||||
if (get_crypt_ftr_and_key(ftr2, key_props)) {
|
||||
SLOGE("Failed to read crypto footer back");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(&ftr, &ftr2, sizeof(ftr)) != 0) {
|
||||
SLOGE("Crypto footer not correctly written");
|
||||
// ex4enc:TODO why is this failing?
|
||||
//return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UnencryptedProperties(path).Remove(properties::ref)) {
|
||||
SLOGE("Failed to remove key ref");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return e4crypt_check_passwd(path, "");
|
||||
}
|
||||
|
||||
int e4crypt_change_password(const char* path, int crypt_type,
|
||||
const char* password)
|
||||
{
|
||||
SLOGI("e4crypt_change_password");
|
||||
|
||||
UnencryptedProperties key_props = GetProps(path).GetChild(properties::key);
|
||||
|
||||
crypt_mnt_ftr ftr;
|
||||
if (get_crypt_ftr_and_key(ftr, key_props)) {
|
||||
SLOGE("Failed to read crypto footer back");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto mki = s_key_store.find(path);
|
||||
if (mki == s_key_store.end()) {
|
||||
SLOGE("No stored master key - can't change password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const unsigned char* master_key
|
||||
= reinterpret_cast<const unsigned char*>(&mki->second.master_key[0]);
|
||||
|
||||
if (cryptfs_set_password(&ftr, password, master_key)) {
|
||||
SLOGE("Failed to set password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ftr.crypt_type = crypt_type;
|
||||
|
||||
if (put_crypt_ftr_and_key(ftr, key_props)) {
|
||||
SLOGE("Failed to write crypto footer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!UnencryptedProperties(path).Set(properties::is_default,
|
||||
crypt_type == CRYPT_TYPE_DEFAULT)) {
|
||||
SLOGE("Failed to update default flag");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_crypto_complete(const char* path)
|
||||
{
|
||||
SLOGI("ext4 crypto complete called on %s", path);
|
||||
UnencryptedProperties key_props
|
||||
= GetPropsOrAltProps(path).GetChild(properties::key);
|
||||
if (key_props.Get<std::string>(tag::master_key).empty()) {
|
||||
SLOGI("No master key, so not ext4enc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_check_passwd(const char* path, const char* password)
|
||||
{
|
||||
SLOGI("e4crypt_check_password");
|
||||
|
||||
// ext4enc:TODO once we have password checking, fix this to be
|
||||
// GetKeyOrAltKey
|
||||
UnencryptedProperties props = *password ? GetAltProps(path)
|
||||
: GetProps(path);
|
||||
UnencryptedProperties key_props = props.GetChild(properties::key);
|
||||
|
||||
crypt_mnt_ftr ftr;
|
||||
if (get_crypt_ftr_and_key(ftr, key_props)) {
|
||||
SLOGE("Failed to read crypto footer back");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char master_key[key_length / 8];
|
||||
if (cryptfs_get_master_key (&ftr, password, master_key)){
|
||||
SLOGI("Incorrect password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s_key_store[path] = keys{std::string(reinterpret_cast<char*>(master_key),
|
||||
sizeof(master_key)),
|
||||
password};
|
||||
|
||||
// Install password into global keyring
|
||||
ext4_encryption_key ext4_key = {0, {0}, key_length / 8};
|
||||
memcpy(ext4_key.raw, master_key, ext4_key.size);
|
||||
|
||||
// ext4enc:TODO Use better reference not 1234567890
|
||||
key_serial_t device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING,
|
||||
"keyring", "e4crypt", 0);
|
||||
|
||||
SLOGI("Found device_keyring - id is %d", device_keyring);
|
||||
|
||||
key_serial_t key_id = add_key("logon", "ext4-key:1234567890",
|
||||
(void*)&ext4_key, sizeof(ext4_key),
|
||||
device_keyring);
|
||||
|
||||
if (key_id == -1) {
|
||||
SLOGE("Failed to insert key into keyring with error %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
SLOGI("Added key %d to keyring %d in process %d",
|
||||
key_id, device_keyring, getpid());
|
||||
|
||||
// ext4enc:TODO set correct permissions
|
||||
long result = keyctl_setperm(key_id, 0x3f3f3f3f);
|
||||
if (result) {
|
||||
SLOGE("KEYCTL_SETPERM failed with error %ld", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Save reference to key so we can set policy later
|
||||
if (!props.Set(properties::ref, "@s.ext4-key:1234567890")) {
|
||||
SLOGE("Cannot save key reference");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_restart(const char* path)
|
||||
{
|
||||
SLOGI("e4crypt_restart");
|
||||
|
||||
int rc = 0;
|
||||
|
||||
SLOGI("ext4 restart called on %s", path);
|
||||
property_set("vold.decrypt", "trigger_reset_main");
|
||||
SLOGI("Just asked init to shut down class main");
|
||||
sleep(2);
|
||||
|
||||
std::string tmp_path = std::string() + path + "/tmp_mnt";
|
||||
|
||||
// ext4enc:TODO add retry logic
|
||||
rc = umount(tmp_path.c_str());
|
||||
if (rc) {
|
||||
SLOGE("umount %s failed with rc %d, msg %s",
|
||||
tmp_path.c_str(), rc, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ext4enc:TODO add retry logic
|
||||
rc = umount(path);
|
||||
if (rc) {
|
||||
SLOGE("umount %s failed with rc %d, msg %s",
|
||||
path, rc, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* e4crypt_get_password(const char* path)
|
||||
{
|
||||
SLOGI("e4crypt_get_password");
|
||||
|
||||
// ext4enc:TODO scrub password after timeout
|
||||
auto i = s_key_store.find(path);
|
||||
if (i == s_key_store.end()) {
|
||||
return 0;
|
||||
} else {
|
||||
return i->second.password.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
int e4crypt_get_password_type(const char* path)
|
||||
{
|
||||
SLOGI("e4crypt_get_password_type");
|
||||
return GetPropsOrAltProps(path).GetChild(properties::key)
|
||||
.Get<int>(tag::crypt_type, CRYPT_TYPE_DEFAULT);
|
||||
}
|
16
Ext4Crypt.h
Normal file
16
Ext4Crypt.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// General functions
|
||||
int e4crypt_enable(const char* path);
|
||||
int e4crypt_main(int argc, char* argv[]);
|
||||
int e4crypt_change_password(const char* path, int crypt_type,
|
||||
const char* password);
|
||||
int e4crypt_crypto_complete(const char* path);
|
||||
int e4crypt_check_passwd(const char* path, const char* password);
|
||||
int e4crypt_get_password_type(const char* path);
|
||||
const char* e4crypt_get_password(const char* path);
|
||||
int e4crypt_restart(const char* path);
|
||||
|
||||
__END_DECLS
|
50
cryptfs.c
50
cryptfs.c
|
@ -53,7 +53,8 @@
|
|||
#include "VolumeManager.h"
|
||||
#include "VoldUtil.h"
|
||||
#include "crypto_scrypt.h"
|
||||
#include "ext4_crypt.h"
|
||||
#include "Ext4Crypt.h"
|
||||
#include "ext4_crypt_init_extensions.h"
|
||||
#include "ext4_utils.h"
|
||||
#include "f2fs_sparseblock.h"
|
||||
#include "CheckBattery.h"
|
||||
|
@ -1345,7 +1346,7 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
|
||||
static int decrypt_master_key_aux(const char *passwd, unsigned char *salt,
|
||||
unsigned char *encrypted_master_key,
|
||||
unsigned char *decrypted_master_key,
|
||||
kdf_func kdf, void *kdf_params,
|
||||
|
@ -1410,7 +1411,7 @@ static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_pa
|
|||
}
|
||||
}
|
||||
|
||||
static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
|
||||
static int decrypt_master_key(const char *passwd, unsigned char *decrypted_master_key,
|
||||
struct crypt_mnt_ftr *crypt_ftr,
|
||||
unsigned char** intermediate_key,
|
||||
size_t* intermediate_key_size)
|
||||
|
@ -3804,3 +3805,46 @@ void cryptfs_clear_password()
|
|||
password_expiry_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cryptfs_enable_file()
|
||||
{
|
||||
return e4crypt_enable(DATA_MNT_POINT);
|
||||
}
|
||||
|
||||
int cryptfs_create_default_ftr(struct crypt_mnt_ftr* crypt_ftr, __attribute__((unused))int key_length)
|
||||
{
|
||||
if (cryptfs_init_crypt_mnt_ftr(crypt_ftr)) {
|
||||
SLOGE("Failed to initialize crypt_ftr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (create_encrypted_random_key(DEFAULT_PASSWORD, crypt_ftr->master_key,
|
||||
crypt_ftr->salt, crypt_ftr)) {
|
||||
SLOGE("Cannot create encrypted master key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//crypt_ftr->keysize = key_length / 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
|
||||
unsigned char* master_key)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// ext4enc:TODO check intermediate_key to see if this is valid key
|
||||
unsigned char* intermediate_key = 0;
|
||||
size_t intermediate_key_size = 0;
|
||||
rc = decrypt_master_key(password, master_key, ftr, &intermediate_key,
|
||||
&intermediate_key_size);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password,
|
||||
const unsigned char* master_key)
|
||||
{
|
||||
return encrypt_master_key(password, ftr->salt, master_key, ftr->master_key,
|
||||
ftr);
|
||||
}
|
||||
|
|
|
@ -235,6 +235,7 @@ extern "C" {
|
|||
int cryptfs_enable(char *flag, int type, char *passwd, int allow_reboot);
|
||||
int cryptfs_changepw(int type, const char *newpw);
|
||||
int cryptfs_enable_default(char *flag, int allow_reboot);
|
||||
int cryptfs_enable_file();
|
||||
int cryptfs_setup_volume(const char *label, int major, int minor,
|
||||
char *crypto_dev_path, unsigned int max_pathlen,
|
||||
int *new_major, int *new_minor);
|
||||
|
@ -245,6 +246,13 @@ extern "C" {
|
|||
int cryptfs_get_password_type(void);
|
||||
const char* cryptfs_get_password(void);
|
||||
void cryptfs_clear_password(void);
|
||||
|
||||
// Functions for file encryption to use to inherit our encryption logic
|
||||
int cryptfs_create_default_ftr(struct crypt_mnt_ftr* ftr, int key_length);
|
||||
int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
|
||||
unsigned char* master_key);
|
||||
int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password,
|
||||
const unsigned char* master_key);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue