Merge "[DO NOT MERGE] Implement key attestation using AndroidKeystore." into pi-dev-plus-aosp
diff --git a/btif/Android.bp b/btif/Android.bp
index 3b4e574..e003725 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -24,6 +24,8 @@
"system/bt/utils/include",
"system/bt/include",
"system/libhwbinder/include",
+ "system/security/keystore/include",
+ "hardware/interfaces/keymaster/4.0/support/include",
]
// libbtif static library for target
@@ -71,6 +73,8 @@
"src/btif_hf_client.cc",
"src/btif_hh.cc",
"src/btif_hd.cc",
+ "src/btif_hl.cc",
+ "src/btif_keystore.cc",
"src/btif_mce.cc",
"src/btif_pan.cc",
"src/btif_profile_queue.cc",
@@ -103,6 +107,12 @@
"libhwbinder",
"libutils",
"libcrypto",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@3.0",
+ "libkeymaster4support",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libkeystore_parcelables",
],
whole_static_libs: [
"avrcp-target-service",
@@ -138,6 +148,12 @@
"libprocessgroup",
"libutils",
"libcrypto",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@3.0",
+ "libkeymaster4support",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libkeystore_parcelables",
],
static_libs: [
"libbt-bta",
diff --git a/btif/include/btif_keystore.h b/btif/include/btif_keystore.h
new file mode 100644
index 0000000..1e5c2ea
--- /dev/null
+++ b/btif/include/btif_keystore.h
@@ -0,0 +1,39 @@
+/******************************************************************************
+ *
+ * Copyright 2019 Google, Inc.
+ *
+ * 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 <base/logging.h>
+#include <keystore/keystore_client_impl.h>
+
+#include "osi/include/alarm.h"
+#include "osi/include/allocator.h"
+#include "osi/include/compat.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+
+using namespace keystore;
+
+class BtifKeystore {
+ public:
+ BtifKeystore();
+ ~BtifKeystore();
+ int Encrypt(const std::string& hash, const std::string& output_filename,
+ int32_t flags);
+ std::string Decrypt(const std::string& input_filename);
+};
diff --git a/btif/src/btif_config.cc b/btif/src/btif_config.cc
index c017c0f..c20b783 100644
--- a/btif/src/btif_config.cc
+++ b/btif/src/btif_config.cc
@@ -23,10 +23,12 @@
#include <base/logging.h>
#include <ctype.h>
#include <openssl/rand.h>
+#include <openssl/sha.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <sstream>
#include <string>
#include <mutex>
@@ -36,6 +38,7 @@
#include "btif_api.h"
#include "btif_common.h"
#include "btif_config_transcode.h"
+#include "btif_keystore.h"
#include "btif_util.h"
#include "common/address_obfuscator.h"
#include "osi/include/alarm.h"
@@ -66,6 +69,8 @@
#else // !defined(OS_GENERIC)
static const char* CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf";
static const char* CONFIG_BACKUP_PATH = "/data/misc/bluedroid/bt_config.bak";
+static const char* CONFIG_FILE_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.conf.encrypted-checksum";
+static const char* CONFIG_BACKUP_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.bak.encrypted-checksum";
static const char* CONFIG_LEGACY_FILE_PATH =
"/data/misc/bluedroid/bt_config.xml";
#endif // defined(OS_GENERIC)
@@ -77,7 +82,14 @@
static void delete_config_files(void);
static void btif_config_remove_unpaired(config_t* config);
static void btif_config_remove_restricted(config_t* config);
-static std::unique_ptr<config_t> btif_config_open(const char* filename);
+static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename);
+
+// Key attestation
+static std::string hash_file(const char* filename);
+static std::string read_checksum_file(const char* filename);
+static void write_checksum_file(const char* filename, const std::string hash);
+static bool verify_hash(const std::string current_hash,
+ const std::string stored_hash);
static enum ConfigSource {
NOT_LOADED,
@@ -157,6 +169,7 @@
static std::recursive_mutex config_lock; // protects operations on |config|.
static std::unique_ptr<config_t> config;
static alarm_t* config_timer;
+static BtifKeystore btifKeystore;
// Module lifecycle functions
@@ -167,12 +180,13 @@
std::string file_source;
- config = btif_config_open(CONFIG_FILE_PATH);
+ config = btif_config_open(CONFIG_FILE_PATH, CONFIG_FILE_CHECKSUM_PATH);
btif_config_source = ORIGINAL;
if (!config) {
LOG_WARN(LOG_TAG, "%s unable to load config file: %s; using backup.",
__func__, CONFIG_FILE_PATH);
- config = btif_config_open(CONFIG_BACKUP_PATH);
+ remove(CONFIG_FILE_CHECKSUM_PATH);
+ config = btif_config_open(CONFIG_BACKUP_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
btif_config_source = BACKUP;
file_source = "Backup";
}
@@ -180,6 +194,7 @@
LOG_WARN(LOG_TAG,
"%s unable to load backup; attempting to transcode legacy file.",
__func__);
+ remove(CONFIG_BACKUP_CHECKSUM_PATH);
config = btif_config_transcode(CONFIG_LEGACY_FILE_PATH);
btif_config_source = LEGACY;
file_source = "Legacy";
@@ -239,7 +254,25 @@
return future_new_immediate(FUTURE_FAIL);
}
-static std::unique_ptr<config_t> btif_config_open(const char* filename) {
+static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename) {
+ // START KEY ATTESTATION
+ // Get hash of current file
+ std::string current_hash = hash_file(filename);
+ // Get stored hash
+ std::string stored_hash = read_checksum_file(checksum_filename);
+ if (stored_hash.empty()) {
+ LOG(ERROR) << __func__ << ": stored_hash=<empty>";
+ if (!current_hash.empty()) {
+ write_checksum_file(checksum_filename, current_hash);
+ stored_hash = read_checksum_file(checksum_filename);
+ }
+ }
+ // Compare hashes
+ if (!verify_hash(current_hash, stored_hash)) {
+ return nullptr;
+ }
+ // END KEY ATTESTATION
+
std::unique_ptr<config_t> config = config_new(filename);
if (!config) return nullptr;
@@ -465,6 +498,13 @@
bool ret = config_save(*config, CONFIG_FILE_PATH);
btif_config_source = RESET;
+
+ // Save encrypted hash
+ std::string current_hash = hash_file(CONFIG_FILE_PATH);
+ if (!current_hash.empty()) {
+ write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
+ }
+
return ret;
}
@@ -482,9 +522,15 @@
std::unique_lock<std::recursive_mutex> lock(config_lock);
rename(CONFIG_FILE_PATH, CONFIG_BACKUP_PATH);
+ rename(CONFIG_FILE_CHECKSUM_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
std::unique_ptr<config_t> config_paired = config_new_clone(*config);
btif_config_remove_unpaired(config_paired.get());
config_save(*config_paired, CONFIG_FILE_PATH);
+ // Save hash
+ std::string current_hash = hash_file(CONFIG_FILE_PATH);
+ if (!current_hash.empty()) {
+ write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
+ }
}
static void btif_config_remove_unpaired(config_t* conf) {
@@ -576,5 +622,57 @@
static void delete_config_files(void) {
remove(CONFIG_FILE_PATH);
remove(CONFIG_BACKUP_PATH);
+ remove(CONFIG_FILE_CHECKSUM_PATH);
+ remove(CONFIG_BACKUP_CHECKSUM_PATH);
osi_property_set("persist.bluetooth.factoryreset", "false");
}
+
+static std::string hash_file(const char* filename) {
+ FILE* fp = fopen(filename, "rb");
+ if (!fp) {
+ LOG(ERROR) << __func__ << ": unable to open config file: '" << filename
+ << "': " << strerror(errno);
+ return "";
+ }
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ const int bufSize = 400 * 10; // initial file is ~400B
+ std::byte* buffer = (std::byte*) osi_calloc(bufSize);
+ int bytesRead = 0;
+ if (!buffer) return "";
+ while ((bytesRead = fread(buffer, 1, bufSize, fp))) {
+ SHA256_Update(&sha256, buffer, bytesRead);
+ }
+ SHA256_Final(hash, &sha256);
+ std::stringstream ss;
+ for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
+ }
+ fclose(fp);
+ osi_free(buffer);
+ return ss.str();
+}
+
+static std::string read_checksum_file(const char* checksum_filename) {
+ // Ensure file exists
+ FILE* fp = fopen(checksum_filename, "rb");
+ if (!fp) {
+ return "";
+ } else {
+ fclose(fp);
+ }
+ std::string output = btifKeystore.Decrypt(checksum_filename);
+ return output;
+}
+
+static void write_checksum_file(const char* checksum_filename, std::string hash) {
+ int result = btifKeystore.Encrypt(hash, checksum_filename, 0);
+ if (result != 0) {
+ LOG(ERROR) << "Failed writing checksum!";
+ }
+}
+
+static bool verify_hash(std::string current_hash, std::string stored_hash) {
+ return current_hash.compare(stored_hash) == 0;
+}
diff --git a/btif/src/btif_keystore.cc b/btif/src/btif_keystore.cc
new file mode 100644
index 0000000..0805d21
--- /dev/null
+++ b/btif/src/btif_keystore.cc
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * Copyright 2019 Google, Inc.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_keystore"
+
+#include "btif_keystore.h"
+#include "osi/include/properties.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/utf_string_conversions.h>
+#include <sys/stat.h>
+
+using namespace keystore;
+
+static std::unique_ptr<keystore::KeystoreClient> CreateKeystoreInstance(void);
+static void WriteFile(const std::string& filename, const std::string& content);
+static std::string ReadFile(const std::string& filename);
+static int GenerateKey(const std::string& name, int32_t flags, bool auth_bound);
+static bool DoesKeyExist(const std::string& name);
+
+const std::string FILE_SUFFIX = ".encrypted-checksum";
+const std::string CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+const std::string DIGEST_ALGORITHM = "SHA-256";
+const std::string KEY_STORE = "AndroidKeystore";
+
+std::unique_ptr<KeystoreClient> keystoreClient;
+
+BtifKeystore::BtifKeystore() { keystoreClient = CreateKeystoreInstance(); }
+
+BtifKeystore::~BtifKeystore() {
+ // Using a smart pointer, does it delete itself?
+ // delete keystoreClient;
+}
+
+int BtifKeystore::Encrypt(const std::string& hash,
+ const std::string& output_filename, int32_t flags) {
+ std::string output;
+ if (!DoesKeyExist(KEY_STORE)) {
+ GenerateKey(KEY_STORE, 0, false);
+ }
+ char is_unittest[PROPERTY_VALUE_MAX] = {0};
+ osi_property_get("debug.bluetooth.unittest", is_unittest, "false");
+ if (strcmp(is_unittest, "false") == 0) {
+ if (!keystoreClient->encryptWithAuthentication(KEY_STORE, hash, flags,
+ &output)) {
+ LOG(ERROR) << "EncryptWithAuthentication failed.\n";
+ return 1;
+ }
+ }
+ WriteFile(output_filename, output);
+ return 0;
+}
+
+std::string BtifKeystore::Decrypt(const std::string& input_filename) {
+ std::string input = ReadFile(input_filename);
+ std::string output;
+
+ char is_unittest[PROPERTY_VALUE_MAX] = {0};
+ osi_property_get("debug.bluetooth.unittest", is_unittest, "false");
+ if (strcmp(is_unittest, "false") == 0) {
+ if (!keystoreClient->decryptWithAuthentication(KEY_STORE, input, &output)) {
+ LOG(ERROR) << "DecryptWithAuthentication failed.\n";
+ }
+ }
+ return output;
+}
+
+static std::string ReadFile(const std::string& filename) {
+ std::string content;
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) == 0) {
+ base::FilePath path(filename);
+ if (!base::ReadFileToString(path, &content)) {
+ LOG(ERROR) << "ReadFile failed.\n" << filename.c_str();
+ }
+ }
+ return content;
+}
+
+static void WriteFile(const std::string& filename, const std::string& content) {
+ base::FilePath path(filename);
+ int size = content.size();
+ if (base::WriteFile(path, content.data(), size) != size) {
+ LOG(ERROR) << "WriteFile failed.\n" << filename.c_str();
+ }
+}
+
+// Note: auth_bound keys created with this tool will not be usable.
+static int GenerateKey(const std::string& name, int32_t flags,
+ bool auth_bound) {
+ AuthorizationSetBuilder params;
+ params.RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_224)
+ .Digest(Digest::SHA_2_256)
+ .Digest(Digest::SHA_2_384)
+ .Digest(Digest::SHA_2_512)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Padding(PaddingMode::RSA_PSS);
+ if (auth_bound) {
+ // Gatekeeper normally generates the secure user id.
+ // Using zero allows the key to be created, but it will not be usuable.
+ params.Authorization(TAG_USER_SECURE_ID, 0);
+ } else {
+ params.Authorization(TAG_NO_AUTH_REQUIRED);
+ }
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+
+ char is_unittest[PROPERTY_VALUE_MAX] = {0};
+ osi_property_get("debug.bluetooth.unittest", is_unittest, "false");
+ if (strcmp(is_unittest, "false") != 0) {
+ return -1;
+ }
+ auto result = keystoreClient->generateKey(name, params, flags,
+ &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ return result.getErrorCode();
+}
+
+static bool DoesKeyExist(const std::string& name) {
+ return keystoreClient->doesKeyExist(name) ? true : false;
+}
+
+static std::unique_ptr<KeystoreClient> CreateKeystoreInstance(void) {
+ return std::unique_ptr<KeystoreClient>(
+ static_cast<KeystoreClient*>(new KeystoreClientImpl));
+}
diff --git a/main/Android.bp b/main/Android.bp
index 942ee9c..8b32562 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -35,6 +35,8 @@
"system/bt/embdrv/sbc/encoder/include",
"system/bt/embdrv/sbc/decoder/include",
"system/bt/utils/include",
+ "system/security/keystore/include",
+ "hardware/interfaces/keymaster/4.0/support/include",
],
logtags: ["../EventLogTags.logtags"],
shared_libs: [
@@ -55,6 +57,12 @@
"libtinyxml2",
"libz",
"libcrypto",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@3.0",
+ "libkeymaster4support",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libkeystore_parcelables",
],
static_libs: [
"libbt-sbc-decoder",
diff --git a/test/suite/adapter/bluetooth_test.cc b/test/suite/adapter/bluetooth_test.cc
index 02132f1..012592f 100644
--- a/test/suite/adapter/bluetooth_test.cc
+++ b/test/suite/adapter/bluetooth_test.cc
@@ -19,6 +19,7 @@
#include "adapter/bluetooth_test.h"
#include <mutex>
#include "btcore/include/property.h"
+#include "osi/include/properties.h"
namespace {
@@ -46,6 +47,8 @@
adapter_state_changed_callback_sem_ = semaphore_new(0);
discovery_state_changed_callback_sem_ = semaphore_new(0);
+ osi_property_set("debug.bluetooth.unittest", "true");
+
bluetooth::hal::BluetoothInterface::Initialize();
ASSERT_TRUE(bluetooth::hal::BluetoothInterface::IsInitialized());
auto bt_hal_interface = bluetooth::hal::BluetoothInterface::Get();
@@ -64,6 +67,8 @@
bt_hal_interface->RemoveObserver(this);
bt_hal_interface->CleanUp();
ASSERT_FALSE(bt_hal_interface->IsInitialized());
+
+ osi_property_set("debug.bluetooth.unittest", "false");
}
void BluetoothTest::ClearSemaphore(semaphore_t* sem) {