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

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

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
parent 57262a9c
Loading
Loading
Loading
Loading
+15 −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
@@ -71,6 +73,7 @@ cc_library_static {
        "src/btif_hf_client.cc",
        "src/btif_hh.cc",
        "src/btif_hd.cc",
        "src/btif_keystore.cc",
        "src/btif_mce.cc",
        "src/btif_pan.cc",
        "src/btif_profile_queue.cc",
@@ -101,6 +104,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",
@@ -133,6 +142,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",
+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);
};
+102 −4
Original line number Diff line number Diff line
@@ -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 @@ 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;

@@ -471,6 +504,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;
}

@@ -488,9 +528,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) {
@@ -582,5 +628,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));
}
+8 −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: [
@@ -53,6 +55,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",
Loading