Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb2db286 authored by Martin Brabham's avatar Martin Brabham
Browse files

[DO NOT MERGE] Implement key attestation using AndroidKeystore.

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
parent ab45924f
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ btifCommonIncludes = [
    "packages/modules/Bluetooth/system/utils/include",
    "packages/modules/Bluetooth/system/include",
    "system/libhwbinder/include",
    "system/security/keystore/include",
    "hardware/interfaces/keymaster/4.0/support/include",
]

// libbtif static library for target
@@ -72,6 +74,7 @@ cc_library_static {
        "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",
@@ -101,6 +104,13 @@ cc_library_static {
        "libhidltransport",
        "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",
@@ -127,6 +137,13 @@ cc_test {
        "libprotobuf-cpp-lite",
        "libcutils",
        "libutils",
        "libcrypto",
        "android.hardware.keymaster@4.0",
        "android.hardware.keymaster@3.0",
        "libkeymaster4support",
        "libkeystore_aidl",
        "libkeystore_binder",
        "libkeystore_parcelables",
    ],
    static_libs: [
        "libbt-bta",
+39 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  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);
};
+103 −4
Original line number Diff line number Diff line
@@ -22,10 +22,13 @@

#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>
@@ -35,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 "osi/include/alarm.h"
#include "osi/include/allocator.h"
@@ -60,6 +64,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)
@@ -71,7 +77,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,
@@ -117,6 +130,7 @@ bool btif_get_address_type(const RawAddress& bda, int* p_addr_type) {
static std::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

@@ -127,12 +141,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";
  }
@@ -140,6 +155,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";
@@ -196,7 +212,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;

@@ -412,6 +446,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;
}

@@ -429,9 +470,15 @@ static void btif_config_write(UNUSED_ATTR uint16_t event,

  std::unique_lock<std::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) {
@@ -523,5 +570,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;
}
+146 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  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));
}
+9 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ cc_library_shared {
        "packages/modules/Bluetooth/system/embdrv/sbc/encoder/include",
        "packages/modules/Bluetooth/system/embdrv/sbc/decoder/include",
        "packages/modules/Bluetooth/system/utils/include",
        "system/security/keystore/include",
        "hardware/interfaces/keymaster/4.0/support/include",
    ],
    logtags: ["../../EventLogTags.logtags"],
    shared_libs: [
@@ -51,6 +53,13 @@ cc_library_shared {
        "libutils",
        "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",
Loading