[DO NOT MERGE] Handle edge cases where input or hash/data could be null.
Bug: b/117993149
Test: Manual; atest net_test_btif net_test_bluetooth
Change-Id: Ia91ea822ee2147b2a2d14bea250a708e8c10bae4
Merged-In: Ia91ea822ee2147b2a2d14bea250a708e8c10bae4
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 257ef7d..a89679d 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -127,7 +127,10 @@
name: "net_test_btif",
defaults: ["fluoride_defaults"],
include_dirs: btifCommonIncludes,
- srcs: ["test/btif_storage_test.cc"],
+ srcs: [
+ "test/btif_storage_test.cc",
+ "test/btif_keystore_test.cc"
+ ],
header_libs: ["libbluetooth_headers"],
shared_libs: [
"libaudioclient",
@@ -144,6 +147,7 @@
"libkeystore_aidl",
"libkeystore_binder",
"libkeystore_parcelables",
+ "libbinder",
],
static_libs: [
"libbt-bta",
@@ -165,6 +169,9 @@
"libbluetooth-for-tests",
],
cflags: ["-DBUILDCFG"],
+ sanitize: {
+ integer_overflow: true,
+ },
}
// btif profile queue unit tests for target
diff --git a/system/btif/include/btif_keystore.h b/system/btif/include/btif_keystore.h
index 1e5c2ea..01ccd66 100644
--- a/system/btif/include/btif_keystore.h
+++ b/system/btif/include/btif_keystore.h
@@ -18,6 +18,7 @@
#include <base/logging.h>
#include <keystore/keystore_client_impl.h>
+#include <mutex>
#include "osi/include/alarm.h"
#include "osi/include/allocator.h"
@@ -27,13 +28,45 @@
#include "osi/include/osi.h"
#include "osi/include/properties.h"
-using namespace keystore;
-
+namespace bluetooth {
+/**
+ * Client wrapper to access AndroidKeystore.
+ *
+ * <p>Use to encrypt/decrypt data and store to disk.
+ */
class BtifKeystore {
public:
- BtifKeystore();
- ~BtifKeystore();
- int Encrypt(const std::string& hash, const std::string& output_filename,
- int32_t flags);
+ /**
+ * @param keystore_client injected pre-created client object for keystore
+ */
+ BtifKeystore(keystore::KeystoreClient* keystore_client);
+
+ /**
+ * Stores encrypted data to disk.
+ *
+ * <p>Returns true on success.
+ *
+ * @param data to be encrypted
+ * @param output_filename location to write the file
+ * @param flags
+ */
+ bool Encrypt(const std::string& data, const std::string& output_filename,
+ int32_t flags);
+
+ /**
+ * Returns a decrypted string representation of the encrypted data or empty
+ * string on error.
+ *
+ * @param input_filename location of file to read and decrypt
+ */
std::string Decrypt(const std::string& input_filename);
+
+ private:
+ std::unique_ptr<keystore::KeystoreClient> keystore_client_;
+ std::mutex api_mutex_;
+ keystore::KeyStoreNativeReturnCode GenerateKey(const std::string& name,
+ int32_t flags,
+ bool auth_bound);
};
+
+} // namespace bluetooth
diff --git a/system/btif/src/btif_config.cc b/system/btif/src/btif_config.cc
index d9c25a6..9145db4 100644
--- a/system/btif/src/btif_config.cc
+++ b/system/btif/src/btif_config.cc
@@ -56,6 +56,10 @@
#define TIME_STRING_LENGTH sizeof("YYYY-MM-DD HH:MM:SS")
static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
+constexpr int kBufferSize = 400 * 10; // initial file is ~400B
+
+using bluetooth::BtifKeystore;
+
// TODO(armansito): Find a better way than searching by a hardcoded path.
#if defined(OS_GENERIC)
static const char* CONFIG_FILE_PATH = "bt_config.conf";
@@ -83,8 +87,6 @@
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,
@@ -130,7 +132,8 @@
static std::mutex config_lock; // protects operations on |config|.
static std::unique_ptr<config_t> config;
static alarm_t* config_timer;
-static BtifKeystore btifKeystore;
+
+static BtifKeystore btif_keystore(new keystore::KeystoreClientImpl);
// Module lifecycle functions
@@ -226,7 +229,7 @@
}
}
// Compare hashes
- if (!verify_hash(current_hash, stored_hash)) {
+ if (current_hash != stored_hash) {
return nullptr;
}
// END KEY ATTESTATION
@@ -582,18 +585,13 @@
<< "': " << strerror(errno);
return "";
}
- unsigned char hash[SHA256_DIGEST_LENGTH];
+ uint8_t 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) {
- fclose(fp);
- return "";
- }
- while ((bytesRead = fread(buffer, 1, bufSize, fp))) {
- SHA256_Update(&sha256, buffer, bytesRead);
+ std::array<unsigned char, kBufferSize> buffer;
+ int bytes_read = 0;
+ while ((bytes_read = fread(buffer.data(), 1, buffer.size(), fp))) {
+ SHA256_Update(&sha256, buffer.data(), bytes_read);
}
SHA256_Final(hash, &sha256);
std::stringstream ss;
@@ -601,7 +599,6 @@
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
}
fclose(fp);
- osi_free(buffer);
return ss.str();
}
@@ -610,19 +607,11 @@
if (access(checksum_filename, R_OK) != 0) {
return "";
}
- std::string output = btifKeystore.Decrypt(checksum_filename);
- return output;
+ return btif_keystore.Decrypt(checksum_filename);
}
static void write_checksum_file(const char* checksum_filename,
const std::string& hash) {
- int result = btifKeystore.Encrypt(hash, checksum_filename, 0);
- if (result != 0) {
- LOG(ERROR) << "Failed writing checksum!";
- }
-}
-
-static bool verify_hash(const std::string& current_hash,
- const std::string& stored_hash) {
- return current_hash.compare(stored_hash) == 0;
+ bool result = btif_keystore.Encrypt(hash, checksum_filename, 0);
+ CHECK(result) << "Failed writing checksum";
}
diff --git a/system/btif/src/btif_keystore.cc b/system/btif/src/btif_keystore.cc
index 0805d21..ac3a803 100644
--- a/system/btif/src/btif_keystore.cc
+++ b/system/btif/src/btif_keystore.cc
@@ -16,10 +16,7 @@
*
******************************************************************************/
-#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>
@@ -30,83 +27,92 @@
#include <sys/stat.h>
using namespace keystore;
+using namespace bluetooth;
-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;
-}
+constexpr char kKeyStore[] = "AndroidKeystore";
static std::string ReadFile(const std::string& filename) {
+ CHECK(!filename.empty()) << __func__ << ": filename should not be empty";
+
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();
- }
+ base::FilePath path(filename);
+ if (!base::PathExists(path)) {
+ // Config file checksum file doesn't exist on first run after OTA.
+ LOG(ERROR) << "file '" << filename.c_str() << "'doesn't exists yet";
+ }
+ if (!base::ReadFileToString(path, &content)) {
+ LOG(ERROR) << "ReadFile failed: " << filename.c_str();
}
return content;
}
static void WriteFile(const std::string& filename, const std::string& content) {
+ CHECK(!filename.empty()) << __func__ << ": filename should not be empty";
+ CHECK(!content.empty()) << __func__ << ": content should not be empty";
+
base::FilePath path(filename);
int size = content.size();
if (base::WriteFile(path, content.data(), size) != size) {
- LOG(ERROR) << "WriteFile failed.\n" << filename.c_str();
+ LOG(FATAL) << "WriteFile failed.\n" << filename.c_str();
}
}
+namespace bluetooth {
+
+BtifKeystore::BtifKeystore(keystore::KeystoreClient* keystore_client)
+ : keystore_client_(keystore_client) {}
+
+bool BtifKeystore::Encrypt(const std::string& data,
+ const std::string& output_filename, int32_t flags) {
+ std::lock_guard<std::mutex> lock(api_mutex_);
+ if (data.empty()) {
+ LOG(ERROR) << __func__ << ": empty data";
+ return false;
+ }
+ if (output_filename.empty()) {
+ LOG(ERROR) << __func__ << ": empty output filename";
+ return false;
+ }
+ std::string output;
+ if (!keystore_client_->doesKeyExist(kKeyStore)) {
+ auto gen_result = GenerateKey(kKeyStore, 0, false);
+ if (!gen_result.isOk()) {
+ LOG(FATAL) << "EncryptWithAuthentication Failed: generateKey response="
+ << gen_result;
+ return false;
+ }
+ }
+ if (!keystore_client_->encryptWithAuthentication(kKeyStore, data, flags,
+ &output)) {
+ LOG(FATAL) << "EncryptWithAuthentication failed.";
+ return false;
+ }
+ WriteFile(output_filename, output);
+ return true;
+}
+
+std::string BtifKeystore::Decrypt(const std::string& input_filename) {
+ std::lock_guard<std::mutex> lock(api_mutex_);
+ std::string output;
+ if (input_filename.empty()) {
+ LOG(ERROR) << __func__ << ": empty input filename";
+ return output;
+ }
+ std::string input = ReadFile(input_filename);
+ if (input.empty()) {
+ LOG(ERROR) << __func__ << ": empty input data";
+ return output;
+ }
+ if (!keystore_client_->decryptWithAuthentication(kKeyStore, input, &output)) {
+ LOG(FATAL) << "DecryptWithAuthentication failed.\n";
+ }
+ return output;
+}
+
// 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) {
+KeyStoreNativeReturnCode BtifKeystore::GenerateKey(const std::string& name,
+ int32_t flags,
+ bool auth_bound) {
AuthorizationSetBuilder params;
params.RsaSigningKey(2048, 65537)
.Digest(Digest::SHA_2_224)
@@ -124,23 +130,9 @@
}
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();
+ return keystore_client_->generateKey(name, params, flags,
+ &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
}
-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));
-}
+} // namespace bluetooth
diff --git a/system/btif/test/btif_keystore_test.cc b/system/btif/test/btif_keystore_test.cc
new file mode 100644
index 0000000..e57c64f
--- /dev/null
+++ b/system/btif/test/btif_keystore_test.cc
@@ -0,0 +1,98 @@
+/******************************************************************************
+ *
+ * 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/files/file_util.h>
+#include <base/logging.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <fstream>
+
+#include "btif/include/btif_keystore.h"
+
+using namespace bluetooth;
+
+constexpr char kFilename[] = "/data/misc/bluedroid/testfile.txt";
+
+class BtifKeystoreTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<BtifKeystore> btif_keystore_;
+ base::FilePath file_path_;
+ BtifKeystoreTest() : file_path_(kFilename) {}
+ void SetUp() override {
+ android::ProcessState::self()->startThreadPool();
+ btif_keystore_ =
+ std::make_unique<BtifKeystore>(static_cast<keystore::KeystoreClient*>(
+ new keystore::KeystoreClientImpl));
+ base::DeleteFile(file_path_, true);
+ };
+ void TearDown() override { btif_keystore_ = nullptr; };
+};
+
+// Encrypt
+TEST_F(BtifKeystoreTest, test_encrypt_decrypt) {
+ std::string hash = "test";
+
+ EXPECT_TRUE(btif_keystore_->Encrypt(hash, kFilename, 0));
+ std::string decrypted_hash = btif_keystore_->Decrypt(kFilename);
+
+ EXPECT_TRUE(base::PathExists(file_path_));
+ EXPECT_EQ(hash, decrypted_hash);
+}
+
+TEST_F(BtifKeystoreTest, test_encrypt_empty_hash) {
+ std::string hash = "";
+
+ EXPECT_FALSE(btif_keystore_->Encrypt(hash, kFilename, 0));
+
+ EXPECT_FALSE(base::PathExists(file_path_));
+}
+
+TEST_F(BtifKeystoreTest, test_encrypt_empty_filename) {
+ std::string hash = "test";
+
+ EXPECT_FALSE(btif_keystore_->Encrypt(hash, "", 0));
+
+ EXPECT_FALSE(base::PathExists(file_path_));
+}
+
+// Decrypt
+TEST_F(BtifKeystoreTest, test_decrypt_empty_hash) {
+ // Only way to get the hash to decrypt is to read it from the file
+ // So make empty file manually
+ std::ofstream outfile(kFilename);
+ outfile.close();
+
+ std::string decrypted_hash = btif_keystore_->Decrypt(kFilename);
+
+ EXPECT_TRUE(decrypted_hash.empty());
+}
+
+TEST_F(BtifKeystoreTest, test_decrypt_file_not_exist) {
+ // Ensure file doesn't exist, then decrypt
+ EXPECT_FALSE(base::PathExists(file_path_));
+
+ std::string decrypted_hash = btif_keystore_->Decrypt(kFilename);
+
+ EXPECT_TRUE(decrypted_hash.empty());
+}
+
+TEST_F(BtifKeystoreTest, test_decrypt_empty_filename) {
+ std::string decrypted_hash = btif_keystore_->Decrypt("");
+
+ EXPECT_TRUE(decrypted_hash.empty());
+}
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index 5904cd5..38b4ebd 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -15,6 +15,8 @@
shared_libs: [
"liblog",
"libcutils",
+ "libbinder",
+ "libutils",
],
static_libs: [
"libbtcore",
@@ -40,6 +42,8 @@
shared_libs: [
"liblog",
"libcutils",
+ "libbinder",
+ "libutils",
],
static_libs: [
"libbtcore",
diff --git a/system/test/suite/adapter/bluetooth_test.cc b/system/test/suite/adapter/bluetooth_test.cc
index 012592f..fdf4a9d 100644
--- a/system/test/suite/adapter/bluetooth_test.cc
+++ b/system/test/suite/adapter/bluetooth_test.cc
@@ -17,6 +17,8 @@
******************************************************************************/
#include "adapter/bluetooth_test.h"
+#include <binder/ProcessState.h>
+#include <stdio.h>
#include <mutex>
#include "btcore/include/property.h"
#include "osi/include/properties.h"
@@ -32,6 +34,7 @@
namespace bttest {
void BluetoothTest::SetUp() {
+ android::ProcessState::self()->startThreadPool();
bt_interface_ = nullptr;
state_ = BT_STATE_OFF;
properties_changed_count_ = 0;
@@ -47,7 +50,8 @@
adapter_state_changed_callback_sem_ = semaphore_new(0);
discovery_state_changed_callback_sem_ = semaphore_new(0);
- osi_property_set("debug.bluetooth.unittest", "true");
+ remove("/data/misc/bluedroid/bt_config.conf.encrypted-checksum");
+ remove("/data/misc/bluedroid/bt_config.bak.encrypted-checksum");
bluetooth::hal::BluetoothInterface::Initialize();
ASSERT_TRUE(bluetooth::hal::BluetoothInterface::IsInitialized());