OSDN Git Service

[DO NOT MERGE] Implement key attestation using AndroidKeystore.
authorMartin Brabham <optedoblivion@google.com>
Fri, 1 Feb 2019 21:42:58 +0000 (13:42 -0800)
committerMartin Brabham <optedoblivion@google.com>
Wed, 13 Mar 2019 16:18:24 +0000 (09:18 -0700)
Store SHA256 hash of the config file in an encrypted file that has been encrypted using the Android Keystore API.

Bug: b/117993149
Test: Manual
Change-Id: I26de9ea05f515d6643a83d11628490fb49e10743
Merged-In: I26de9ea05f515d6643a83d11628490fb49e10743

btif/Android.bp
btif/include/btif_keystore.h [new file with mode: 0644]
btif/src/btif_config.cc
btif/src/btif_keystore.cc [new file with mode: 0644]
main/Android.bp
test/suite/adapter/bluetooth_test.cc

index 3b4e574..e003725 100644 (file)
@@ -24,6 +24,8 @@ btifCommonIncludes = [
     "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 @@ cc_library_static {
         "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 @@ cc_library_static {
         "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 @@ cc_test {
         "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 (file)
index 0000000..1e5c2ea
--- /dev/null
@@ -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);
+};
index c017c0f..c20b783 100644 (file)
 #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 @@ static const char* CONFIG_LEGACY_FILE_PATH = "bt_config.xml";
 #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 bool is_factory_reset(void);
 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 void read_or_set_metrics_salt() {
 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 @@ static future_t* init(void) {
 
   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 @@ static future_t* init(void) {
     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 @@ error:
   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 btif_config_clear(void) {
 
   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 @@ static void btif_config_write(UNUSED_ATTR uint16_t event,
 
   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 bool is_factory_reset(void) {
 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 (file)
index 0000000..0805d21
--- /dev/null
@@ -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));
+}
index 942ee9c..8b32562 100644 (file)
@@ -35,6 +35,8 @@ cc_library_shared {
         "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 @@ cc_library_shared {
         "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",
index 02132f1..012592f 100644 (file)
@@ -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 @@ void BluetoothTest::SetUp() {
   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 @@ void BluetoothTest::TearDown() {
   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) {