Use a keymaster-based key storage module
Instead of writing raw keys, encrypt the keys with keymaster. This paves the way to protecting them with auth tokens and passwords later. In addition, fold in the hash of a 16k file into their encryption, to ensure secure deletion works properly. Now even C++ier! Bug: 22502684 Bug: 22950892 Change-Id: If70f139e342373533c42d5a298444b8438428322
This commit is contained in:
parent
a042cb5761
commit
1ef255816c
6 changed files with 611 additions and 65 deletions
|
@ -27,6 +27,8 @@ common_src_files := \
|
|||
MoveTask.cpp \
|
||||
Benchmark.cpp \
|
||||
TrimTask.cpp \
|
||||
Keymaster.cpp \
|
||||
KeyStorage.cpp \
|
||||
|
||||
common_c_includes := \
|
||||
system/extras/ext4_utils \
|
||||
|
@ -52,7 +54,9 @@ common_shared_libraries := \
|
|||
libutils \
|
||||
libhardware \
|
||||
libsoftkeymaster \
|
||||
libbase
|
||||
libbase \
|
||||
libkeymaster1 \
|
||||
libkeymaster_messages \
|
||||
|
||||
common_static_libraries := \
|
||||
libfs_mgr \
|
||||
|
@ -61,7 +65,7 @@ common_static_libraries := \
|
|||
libsquashfs_utils \
|
||||
libscrypt_static \
|
||||
libmincrypt \
|
||||
libbatteryservice
|
||||
libbatteryservice \
|
||||
|
||||
vold_conlyflags := -std=c11
|
||||
vold_cflags := -Werror -Wall -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter
|
||||
|
|
101
Ext4Crypt.cpp
101
Ext4Crypt.cpp
|
@ -16,11 +16,11 @@
|
|||
|
||||
#include "Ext4Crypt.h"
|
||||
|
||||
#include "KeyStorage.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -557,40 +557,20 @@ static bool e4crypt_is_key_ephemeral(const std::string &key_path) {
|
|||
return s_ephemeral_user_keys.find(key_path) != s_ephemeral_user_keys.end();
|
||||
}
|
||||
|
||||
static std::string read_user_key(userid_t user_id)
|
||||
static bool read_user_key(userid_t user_id, std::string &key)
|
||||
{
|
||||
const auto key_path = get_key_path(user_id);
|
||||
const auto ephemeral_key_it = s_ephemeral_user_keys.find(key_path);
|
||||
if (ephemeral_key_it != s_ephemeral_user_keys.end()) {
|
||||
return ephemeral_key_it->second;
|
||||
key = ephemeral_key_it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(key_path, &content)) {
|
||||
return "";
|
||||
if (!android::vold::RetrieveKey(key_path, key)) return false;
|
||||
if (key.size() != key_length/8) {
|
||||
LOG(ERROR) << "Wrong size key " << key.size() << " in " << key_path;
|
||||
return false;
|
||||
}
|
||||
if (content.size() != key_length/8) {
|
||||
LOG(ERROR) << "Wrong size key " << content.size() << " in " << key_path;
|
||||
return "";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// ext4enc:TODO this can't be the only place keys are read from /dev/urandom
|
||||
// we should unite those places.
|
||||
static std::string get_random_string(size_t length) {
|
||||
std::ifstream urandom("/dev/urandom");
|
||||
if (!urandom) {
|
||||
PLOG(ERROR) << "Unable to open /dev/urandom";
|
||||
return "";
|
||||
}
|
||||
std::string res(length, '\0');
|
||||
urandom.read(&res[0], length);
|
||||
if (!urandom) {
|
||||
PLOG(ERROR) << "Unable to read from /dev/urandom";
|
||||
return "";
|
||||
}
|
||||
return res;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_user_key(userid_t user_id, bool create_ephemeral) {
|
||||
|
@ -599,15 +579,16 @@ static bool create_user_key(userid_t user_id, bool create_ephemeral) {
|
|||
return false;
|
||||
}
|
||||
const auto key_path = get_key_path(user_id);
|
||||
auto key = get_random_string(key_length / 8);
|
||||
if (key.empty()) {
|
||||
std::string key;
|
||||
if (android::vold::ReadRandomBytes(key_length / 8, key) != 0) {
|
||||
// TODO status_t plays badly with PLOG, fix it.
|
||||
LOG(ERROR) << "Random read failed";
|
||||
return false;
|
||||
}
|
||||
if (create_ephemeral) {
|
||||
// If the key should be created as ephemeral, store it in memory only.
|
||||
s_ephemeral_user_keys[key_path] = key;
|
||||
} else if (!android::base::WriteStringToFile(key, key_path)) {
|
||||
PLOG(ERROR) << "Unable to write key to " << key_path;
|
||||
} else if (!android::vold::StoreKey(key_path, key)) {
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "Created key " << key_path;
|
||||
|
@ -627,7 +608,8 @@ static int e4crypt_set_user_policy(userid_t user_id, int serial, std::string& pa
|
|||
|
||||
int e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
|
||||
LOG(DEBUG) << "e4crypt_vold_create_user_key for " << user_id << " serial " << serial;
|
||||
if (!read_user_key(user_id).empty()) {
|
||||
std::string key;
|
||||
if (read_user_key(user_id, key)) {
|
||||
LOG(ERROR) << "Already exists, can't e4crypt_vold_create_user_key for "
|
||||
<< user_id << " serial " << serial;
|
||||
// FIXME should we fail the command?
|
||||
|
@ -643,40 +625,34 @@ int e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_destroy_user_key(userid_t user_id) {
|
||||
LOG(DEBUG) << "e4crypt_destroy_user_key(" << user_id << ")";
|
||||
// TODO: destroy second key for user_de data
|
||||
static bool evict_user_key(userid_t user_id) {
|
||||
auto key_path = get_key_path(user_id);
|
||||
auto key = read_user_key(user_id);
|
||||
std::string key;
|
||||
if (!read_user_key(user_id, key)) return false;
|
||||
auto ext4_key = fill_key(key);
|
||||
auto ref = keyname(generate_key_ref(ext4_key.raw, ext4_key.size));
|
||||
auto key_serial = keyctl_search(e4crypt_keyring(), "logon", ref.c_str(), 0);
|
||||
if (keyctl_revoke(key_serial) == 0) {
|
||||
LOG(DEBUG) << "Revoked key with serial " << key_serial << " ref " << ref;
|
||||
} else {
|
||||
if (keyctl_revoke(key_serial) != 0) {
|
||||
PLOG(ERROR) << "Failed to revoke key with serial " << key_serial << " ref " << ref;
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "Revoked key with serial " << key_serial << " ref " << ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
int e4crypt_destroy_user_key(userid_t user_id) {
|
||||
LOG(DEBUG) << "e4crypt_destroy_user_key(" << user_id << ")";
|
||||
// TODO: destroy second key for user_de data
|
||||
bool evict_success = evict_user_key(user_id);
|
||||
auto key_path = get_key_path(user_id);
|
||||
if (e4crypt_is_key_ephemeral(key_path)) {
|
||||
s_ephemeral_user_keys.erase(key_path);
|
||||
return 0;
|
||||
} else {
|
||||
if (!android::vold::DestroyKey(key_path)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int pid = fork();
|
||||
if (pid < 0) {
|
||||
PLOG(ERROR) << "Unable to fork";
|
||||
return -1;
|
||||
}
|
||||
if (pid == 0) {
|
||||
LOG(DEBUG) << "Forked for secdiscard";
|
||||
execl("/system/bin/secdiscard",
|
||||
"/system/bin/secdiscard",
|
||||
"--",
|
||||
key_path.c_str(),
|
||||
NULL);
|
||||
PLOG(ERROR) << "Unable to launch secdiscard on " << key_path;
|
||||
exit(-1);
|
||||
}
|
||||
// ext4enc:TODO reap the zombie
|
||||
return 0;
|
||||
return evict_success ? 0 : -1;
|
||||
}
|
||||
|
||||
static int emulated_lock(const std::string& path) {
|
||||
|
@ -712,8 +688,8 @@ static int emulated_unlock(const std::string& path, mode_t mode) {
|
|||
int e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token) {
|
||||
LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " " << (token != nullptr);
|
||||
if (e4crypt_is_native()) {
|
||||
auto user_key = read_user_key(user_id);
|
||||
if (user_key.empty()) {
|
||||
std::string user_key;
|
||||
if (!read_user_key(user_id, user_key)) {
|
||||
// FIXME special case for user 0
|
||||
if (user_id != 0) {
|
||||
LOG(ERROR) << "Couldn't read key for " << user_id;
|
||||
|
@ -723,8 +699,7 @@ int e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token) {
|
|||
if (!create_user_key(user_id, false)) {
|
||||
return -1;
|
||||
}
|
||||
user_key = read_user_key(user_id);
|
||||
if (user_key.empty()) {
|
||||
if (!read_user_key(user_id, user_key)) {
|
||||
LOG(ERROR) << "Couldn't read just-created key for " << user_id;
|
||||
return -1;
|
||||
}
|
||||
|
|
254
KeyStorage.cpp
Normal file
254
KeyStorage.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 "KeyStorage.h"
|
||||
|
||||
#include "Keymaster.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <keymaster/authorization_set.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
static constexpr size_t AES_KEY_BYTES = 32;
|
||||
static constexpr size_t GCM_NONCE_BYTES = 12;
|
||||
static constexpr size_t GCM_MAC_BYTES = 16;
|
||||
// FIXME: better name than "secdiscardable" sought!
|
||||
static constexpr size_t SECDISCARDABLE_BYTES = 1<<14;
|
||||
|
||||
static const char* kRmPath = "/system/bin/rm";
|
||||
static const char* kSecdiscardPath = "/system/bin/secdiscard";
|
||||
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
|
||||
static const char* kFn_encrypted_key = "encrypted_key";
|
||||
static const char* kFn_secdiscardable = "secdiscardable";
|
||||
|
||||
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 " << actual;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string HashSecdiscardable(const std::string &secdiscardable) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
// Personalise the hashing by introducing a fixed prefix.
|
||||
// Hashing applications should use personalization except when there is a
|
||||
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
|
||||
std::string secdiscardable_hashing_prefix = "Android secdiscardable SHA512";
|
||||
secdiscardable_hashing_prefix.resize(SHA512_CBLOCK);
|
||||
SHA512_Update(&c, secdiscardable_hashing_prefix.data(), secdiscardable_hashing_prefix.size());
|
||||
SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
|
||||
std::string res(SHA512_DIGEST_LENGTH, '\0');
|
||||
SHA512_Final(reinterpret_cast<uint8_t *>(&res[0]), &c);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool GenerateKeymasterKey(Keymaster &keymaster,
|
||||
const keymaster::AuthorizationSet &extra_params,
|
||||
std::string &key) {
|
||||
keymaster::AuthorizationSetBuilder param_builder;
|
||||
param_builder
|
||||
.AesEncryptionKey(AES_KEY_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
|
||||
.Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE)
|
||||
.Authorization(keymaster::TAG_NO_AUTH_REQUIRED); // FIXME integrate with gatekeeper
|
||||
auto params = param_builder.build();
|
||||
params.push_back(extra_params);
|
||||
return keymaster.GenerateKey(params, key);
|
||||
}
|
||||
|
||||
static bool EncryptWithKeymasterKey(
|
||||
Keymaster &keymaster,
|
||||
const std::string &key,
|
||||
const keymaster::AuthorizationSet &extra_params,
|
||||
const std::string &message,
|
||||
std::string &ciphertext) {
|
||||
// FIXME fix repetition
|
||||
keymaster::AuthorizationSetBuilder param_builder;
|
||||
param_builder
|
||||
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
|
||||
.Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
|
||||
auto params = param_builder.build();
|
||||
params.push_back(extra_params);
|
||||
keymaster::AuthorizationSet out_params;
|
||||
auto op_handle = keymaster.Begin(KM_PURPOSE_ENCRYPT, key, params, out_params);
|
||||
if (!op_handle) return false;
|
||||
keymaster_blob_t nonce_blob;
|
||||
if (!out_params.GetTagValue(keymaster::TAG_NONCE, &nonce_blob)) {
|
||||
LOG(ERROR) << "GCM encryption but no nonce generated";
|
||||
return false;
|
||||
}
|
||||
// nonce_blob here is just a pointer into existing data, must not be freed
|
||||
std::string nonce(reinterpret_cast<const char *>(nonce_blob.data), nonce_blob.data_length);
|
||||
if (!CheckSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
|
||||
std::string body;
|
||||
if (!op_handle.UpdateCompletely(message, body)) return false;
|
||||
|
||||
std::string mac;
|
||||
if (!op_handle.FinishWithOutput(mac)) return false;
|
||||
if (!CheckSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
|
||||
ciphertext = nonce + body + mac;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DecryptWithKeymasterKey(
|
||||
Keymaster &keymaster, const std::string &key,
|
||||
const keymaster::AuthorizationSet &extra_params,
|
||||
const std::string &ciphertext,
|
||||
std::string &message) {
|
||||
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
|
||||
auto body_mac = ciphertext.substr(GCM_NONCE_BYTES);
|
||||
// FIXME fix repetition
|
||||
keymaster::AuthorizationSetBuilder param_builder;
|
||||
param_builder
|
||||
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
|
||||
.Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
|
||||
AddStringParam(param_builder, keymaster::TAG_NONCE, nonce);
|
||||
auto params = param_builder.build();
|
||||
params.push_back(extra_params);
|
||||
|
||||
auto op_handle = keymaster.Begin(KM_PURPOSE_DECRYPT, key, params);
|
||||
if (!op_handle) return false;
|
||||
if (!op_handle.UpdateCompletely(body_mac, message)) return false;
|
||||
if (!op_handle.Finish()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StoreKey(const std::string &dir, const std::string &key) {
|
||||
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
|
||||
PLOG(ERROR) << "key mkdir " << dir;
|
||||
return false;
|
||||
}
|
||||
std::string secdiscardable;
|
||||
if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != 0) {
|
||||
// TODO status_t plays badly with PLOG, fix it.
|
||||
LOG(ERROR) << "Random read failed";
|
||||
return false;
|
||||
}
|
||||
// FIXME create a wrapper around reads and writes which handles error logging
|
||||
if (!android::base::WriteStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) {
|
||||
PLOG(ERROR) << "Unable to write secdiscardable to " << dir;
|
||||
return false;
|
||||
}
|
||||
keymaster::AuthorizationSetBuilder param_builder;
|
||||
AddStringParam(param_builder, keymaster::TAG_APPLICATION_ID,
|
||||
HashSecdiscardable(secdiscardable));
|
||||
auto extra_params = param_builder.build();
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
std::string km_key;
|
||||
if (!GenerateKeymasterKey(keymaster, extra_params, km_key)) return false;
|
||||
std::string encrypted_key;
|
||||
if (!EncryptWithKeymasterKey(
|
||||
keymaster, km_key, extra_params, key, encrypted_key)) return false;
|
||||
if (!android::base::WriteStringToFile(km_key, dir + "/" + kFn_keymaster_key_blob)) {
|
||||
PLOG(ERROR) << "Unable to write keymaster_key_blob to " << dir;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteStringToFile(encrypted_key, dir + "/" + kFn_encrypted_key)) {
|
||||
PLOG(ERROR) << "Unable to write encrypted_key to " << dir;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RetrieveKey(const std::string &dir, std::string &key) {
|
||||
std::string secdiscardable;
|
||||
if (!android::base::ReadFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) {
|
||||
PLOG(ERROR) << "Unable to read secdiscardable from " << dir;
|
||||
return false;
|
||||
}
|
||||
keymaster::AuthorizationSetBuilder param_builder;
|
||||
AddStringParam(param_builder, keymaster::TAG_APPLICATION_ID,
|
||||
HashSecdiscardable(secdiscardable));
|
||||
auto extra_params = param_builder.build();
|
||||
std::string km_key;
|
||||
if (!android::base::ReadFileToString(dir + "/" + kFn_keymaster_key_blob, &km_key)) {
|
||||
PLOG(ERROR) << "Unable to read keymaster_key_blob from " << dir;
|
||||
return false;
|
||||
}
|
||||
std::string encrypted_message;
|
||||
if (!android::base::ReadFileToString(dir + "/" + kFn_encrypted_key, &encrypted_message)) {
|
||||
PLOG(ERROR) << "Unable to read encrypted_key to " << dir;
|
||||
return false;
|
||||
}
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
return DecryptWithKeymasterKey(keymaster, km_key, extra_params, encrypted_message, key);
|
||||
}
|
||||
|
||||
static bool DeleteKey(const std::string &dir) {
|
||||
std::string km_key;
|
||||
if (!android::base::ReadFileToString(dir + "/" + kFn_keymaster_key_blob, &km_key)) {
|
||||
PLOG(ERROR) << "Unable to read keymaster_key_blob from " << dir;
|
||||
return false;
|
||||
}
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
if (!keymaster.DeleteKey(km_key)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SecdiscardSecdiscardable(const std::string &dir) {
|
||||
if (ForkExecvp(std::vector<std::string> {
|
||||
kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
|
||||
LOG(ERROR) << "secdiscard failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool RecursiveDeleteKey(const std::string &dir) {
|
||||
if (ForkExecvp(std::vector<std::string> {
|
||||
kRmPath, "-rf", dir}) != 0) {
|
||||
LOG(ERROR) << "recursive delete failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DestroyKey(const std::string &dir) {
|
||||
bool success = true;
|
||||
// Try each thing, even if previous things failed.
|
||||
success &= DeleteKey(dir);
|
||||
success &= SecdiscardSecdiscardable(dir);
|
||||
success &= RecursiveDeleteKey(dir);
|
||||
return success;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
40
KeyStorage.h
Normal file
40
KeyStorage.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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_KEYSTORAGE_H
|
||||
#define ANDROID_VOLD_KEYSTORAGE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
// 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
|
||||
// can be securely deleted.
|
||||
// It's safe to move/rename the directory after creation.
|
||||
bool StoreKey(const std::string &target_dir, const std::string &key);
|
||||
|
||||
// Retrieve the key from the named directory.
|
||||
bool RetrieveKey(const std::string &dir, std::string &key);
|
||||
|
||||
// Securely destroy the key stored in the named directory and delete the directory.
|
||||
bool DestroyKey(const std::string &dir);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
159
Keymaster.cpp
Normal file
159
Keymaster.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 "Keymaster.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
bool KeymasterOperation::UpdateCompletely(
|
||||
const std::string &input,
|
||||
std::string &output) {
|
||||
output.clear();
|
||||
auto it = input.begin();
|
||||
while (it != input.end()) {
|
||||
size_t to_read = static_cast<size_t>(input.end() - it);
|
||||
keymaster_blob_t input_blob {reinterpret_cast<const uint8_t *>(&*it), to_read};
|
||||
keymaster_blob_t output_blob;
|
||||
size_t input_consumed;
|
||||
auto error = device->update(device, op_handle,
|
||||
nullptr, &input_blob, &input_consumed, nullptr, &output_blob);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "update failed, code " << error;
|
||||
device = nullptr;
|
||||
return false;
|
||||
}
|
||||
output.append(reinterpret_cast<const char *>(output_blob.data), output_blob.data_length);
|
||||
free(const_cast<uint8_t *>(output_blob.data));
|
||||
if (input_consumed > to_read) {
|
||||
LOG(ERROR) << "update reported too much input consumed";
|
||||
device = nullptr;
|
||||
return false;
|
||||
}
|
||||
it += input_consumed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeymasterOperation::Finish() {
|
||||
auto error = device->finish(device, op_handle,
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
device = nullptr;
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "finish failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeymasterOperation::FinishWithOutput(std::string &output) {
|
||||
keymaster_blob_t output_blob;
|
||||
auto error = device->finish(device, op_handle,
|
||||
nullptr, nullptr, nullptr, &output_blob);
|
||||
device = nullptr;
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "finish failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
output.assign(reinterpret_cast<const char *>(output_blob.data), output_blob.data_length);
|
||||
free(const_cast<uint8_t *>(output_blob.data));
|
||||
return true;
|
||||
}
|
||||
|
||||
Keymaster::Keymaster() {
|
||||
device = nullptr;
|
||||
const hw_module_t *module;
|
||||
int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "hw_get_module_by_class returned " << ret;
|
||||
return;
|
||||
}
|
||||
// TODO: This will need to be updated to support keymaster2.
|
||||
if (module->module_api_version != KEYMASTER_MODULE_API_VERSION_1_0) {
|
||||
LOG(ERROR) << "module_api_version is " << module->module_api_version;
|
||||
return;
|
||||
}
|
||||
ret = keymaster1_open(module, &device);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "keymaster1_open returned " << ret;
|
||||
device = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Keymaster::GenerateKey(
|
||||
const keymaster::AuthorizationSet &in_params,
|
||||
std::string &key) {
|
||||
keymaster_key_blob_t key_blob;
|
||||
auto error = device->generate_key(device, &in_params, &key_blob, nullptr);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "generate_key failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
key.assign(reinterpret_cast<const char *>(key_blob.key_material), key_blob.key_material_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Keymaster::DeleteKey(const std::string &key) {
|
||||
if (device->delete_key == nullptr) return true;
|
||||
keymaster_key_blob_t key_blob { reinterpret_cast<const uint8_t *>(key.data()), key.size() };
|
||||
auto error = device->delete_key(device, &key_blob);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "delete_key failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
KeymasterOperation Keymaster::Begin(
|
||||
keymaster_purpose_t purpose,
|
||||
const std::string &key,
|
||||
const keymaster::AuthorizationSet &in_params,
|
||||
keymaster::AuthorizationSet &out_params) {
|
||||
keymaster_key_blob_t key_blob { reinterpret_cast<const uint8_t *>(key.data()), key.size() };
|
||||
keymaster_operation_handle_t op_handle;
|
||||
keymaster_key_param_set_t out_params_set;
|
||||
auto error = device->begin(device, purpose,
|
||||
&key_blob, &in_params, &out_params_set, &op_handle);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "begin failed, code " << error;
|
||||
return KeymasterOperation(nullptr, op_handle);
|
||||
}
|
||||
out_params.Clear();
|
||||
out_params.push_back(out_params_set);
|
||||
keymaster_free_param_set(&out_params_set);
|
||||
return KeymasterOperation(device, op_handle);
|
||||
}
|
||||
|
||||
KeymasterOperation Keymaster::Begin(
|
||||
keymaster_purpose_t purpose,
|
||||
const std::string &key,
|
||||
const keymaster::AuthorizationSet &in_params) {
|
||||
keymaster_key_blob_t key_blob { reinterpret_cast<const uint8_t *>(key.data()), key.size() };
|
||||
keymaster_operation_handle_t op_handle;
|
||||
auto error = device->begin(device, purpose,
|
||||
&key_blob, &in_params, nullptr, &op_handle);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "begin failed, code " << error;
|
||||
return KeymasterOperation(nullptr, op_handle);
|
||||
}
|
||||
return KeymasterOperation(device, op_handle);
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
114
Keymaster.h
Normal file
114
Keymaster.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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_KEYMASTER1_H
|
||||
#define ANDROID_VOLD_KEYMASTER1_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/keymaster1.h>
|
||||
|
||||
#include <keymaster/authorization_set.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
using namespace keymaster;
|
||||
|
||||
// C++ wrappers to the keymaster1 C interface.
|
||||
// This is tailored to the needs of KeyStorage, but could be extended to be
|
||||
// a more general interface.
|
||||
|
||||
|
||||
// Wrapper for a keymaster_operation_handle_t representing an
|
||||
// ongoing Keymaster operation. Aborts the operation
|
||||
// in the destructor if it is unfinished. Methods log failures
|
||||
// to LOG(ERROR).
|
||||
class KeymasterOperation {
|
||||
public:
|
||||
~KeymasterOperation() { if (device) device->abort(device, op_handle); }
|
||||
// Is this instance valid? This is false if creation fails, and becomes
|
||||
// false on finish or if an update fails.
|
||||
explicit operator bool() {return device != nullptr;}
|
||||
// Call "update" repeatedly until all of the input is consumed, and
|
||||
// concatenate the output. Return true on success.
|
||||
bool UpdateCompletely(const std::string &input, std::string &output);
|
||||
// Finish; pass nullptr for the "output" param.
|
||||
bool Finish();
|
||||
// Finish and write the output to this string.
|
||||
bool FinishWithOutput(std::string &output);
|
||||
// Move constructor
|
||||
KeymasterOperation(KeymasterOperation&& rhs) {
|
||||
op_handle = rhs.op_handle;
|
||||
device = rhs.device;
|
||||
rhs.device = nullptr;
|
||||
}
|
||||
// Move assignation.
|
||||
KeymasterOperation& operator=(KeymasterOperation&& rhs) {
|
||||
op_handle = rhs.op_handle;
|
||||
device = rhs.device;
|
||||
rhs.device = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
KeymasterOperation(keymaster1_device_t *d, keymaster_operation_handle_t h):
|
||||
device {d}, op_handle {h} {}
|
||||
keymaster1_device_t *device;
|
||||
keymaster_operation_handle_t op_handle;
|
||||
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
|
||||
friend class Keymaster;
|
||||
};
|
||||
|
||||
// Wrapper for a keymaster1_device_t representing an open connection
|
||||
// to the keymaster, which is closed in the destructor.
|
||||
class Keymaster {
|
||||
public:
|
||||
Keymaster();
|
||||
~Keymaster() { if (device) keymaster1_close(device); }
|
||||
// false if we failed to open the keymaster device.
|
||||
explicit operator bool() {return device != nullptr;}
|
||||
// Generate a key in the keymaster from the given params.
|
||||
bool GenerateKey(const AuthorizationSet &in_params, std::string &key);
|
||||
// If the keymaster supports it, permanently delete a key.
|
||||
bool DeleteKey(const std::string &key);
|
||||
// Begin a new cryptographic operation, collecting output parameters.
|
||||
KeymasterOperation Begin(
|
||||
keymaster_purpose_t purpose,
|
||||
const std::string &key,
|
||||
const AuthorizationSet &in_params,
|
||||
AuthorizationSet &out_params);
|
||||
// Begin a new cryptographic operation; don't collect output parameters.
|
||||
KeymasterOperation Begin(
|
||||
keymaster_purpose_t purpose,
|
||||
const std::string &key,
|
||||
const AuthorizationSet &in_params);
|
||||
private:
|
||||
keymaster1_device_t *device;
|
||||
DISALLOW_COPY_AND_ASSIGN(Keymaster);
|
||||
};
|
||||
|
||||
template <keymaster_tag_t Tag>
|
||||
inline AuthorizationSetBuilder& AddStringParam(AuthorizationSetBuilder ¶ms,
|
||||
TypedTag<KM_BYTES, Tag> tag, const std::string& val) {
|
||||
return params.Authorization(tag, val.data(), val.size());
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue