diff options
author | Gaurav Shah <gauravsh@google.com> | 2015-11-02 19:28:18 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-11-02 19:28:18 +0000 |
commit | e01125c8a96e35e22783fcb73fdcaa7f129463c0 (patch) | |
tree | 2a6e7b1e61c7ebd24dc805697d70b4ed2062908e | |
parent | 4122b99ba0cdaeb8bf4a348275de94055fc5df10 (diff) | |
parent | 251cb28132e456f81374c8f8a983a5a9ad9aaee8 (diff) | |
download | security-e01125c8a96e35e22783fcb73fdcaa7f129463c0.tar.gz |
Merge "Add encryption convenience methods to KeystoreClient."
-rw-r--r-- | keystore/Android.mk | 12 | ||||
-rw-r--r-- | keystore/include/keystore/keystore_client.h | 39 | ||||
-rw-r--r-- | keystore/include/keystore/keystore_client_impl.h | 39 | ||||
-rw-r--r-- | keystore/include/keystore/keystore_client_mock.h | 11 | ||||
-rw-r--r-- | keystore/keystore_cli_v2.cpp | 58 | ||||
-rw-r--r-- | keystore/keystore_client.proto | 26 | ||||
-rw-r--r-- | keystore/keystore_client_impl.cpp | 302 |
7 files changed, 471 insertions, 16 deletions
diff --git a/keystore/Android.mk b/keystore/Android.mk index 6c25b892..2daf131b 100644 --- a/keystore/Android.mk +++ b/keystore/Android.mk @@ -16,6 +16,12 @@ LOCAL_PATH := $(call my-dir) +# This has to be lazy-resolved because it depends on the LOCAL_MODULE_CLASS +# which varies depending on what is being built. +define keystore_proto_include +$(call local-generated-sources-dir)/proto/$(LOCAL_PATH) +endef + include $(CLEAR_VARS) ifeq ($(USE_32_BIT_KEYSTORE), true) LOCAL_MULTILIB := 32 @@ -65,6 +71,7 @@ LOCAL_SHARED_LIBRARIES := \ libkeystore_binder LOCAL_MODULE := keystore_cli_v2 LOCAL_MODULE_TAGS := debug +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/gtest/include LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_EXECUTABLE) @@ -77,17 +84,20 @@ LOCAL_CFLAGS := -Wall -Wextra -Werror LOCAL_SRC_FILES := \ IKeystoreService.cpp \ keyblob_utils.cpp \ + keystore_client.proto \ keystore_client_impl.cpp \ keystore_get.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libkeymaster_messages \ liblog \ + libprotobuf-cpp-lite \ libsoftkeymasterdevice \ libutils +LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_MODULE := libkeystore_binder LOCAL_MODULE_TAGS := optional -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(call keystore_proto_include) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_SHARED_LIBRARY) diff --git a/keystore/include/keystore/keystore_client.h b/keystore/include/keystore/keystore_client.h index f87e9af0..cec29f71 100644 --- a/keystore/include/keystore/keystore_client.h +++ b/keystore/include/keystore/keystore_client.h @@ -53,6 +53,33 @@ class KeystoreClient { KeystoreClient() = default; virtual ~KeystoreClient() = default; + // Encrypts and authenticates |data| with minimal configuration for local + // decryption. If a key identified by |key_name| does not already exist it + // will be generated. On success returns true and populates |encrypted_data|. + // Note: implementations may generate more than one key but they will always + // have |key_name| as a prefix. + virtual bool encryptWithAuthentication(const std::string& key_name, const std::string& data, + std::string* encrypted_data) = 0; + + // Decrypts and authenticates |encrypted_data| as output by + // EncryptWithAuthentication using the key(s) identified by |key_name|. On + // success returns true and populates |data|. + virtual bool decryptWithAuthentication(const std::string& key_name, + const std::string& encrypted_data, + std::string* data) = 0; + + // Performs a Begin/Update/Finish sequence for an operation. The |purpose|, + // |key_name|, |input_parameters|, and |output_parameters| are as in + // BeginOperation. The |input_data| is as in UpdateOperation. The + // |signature_to_verify| and |output_data| are as in FinishOperation. On + // success returns true. + virtual bool oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name, + const keymaster::AuthorizationSet& input_parameters, + const std::string& input_data, + const std::string& signature_to_verify, + keymaster::AuthorizationSet* output_parameters, + std::string* output_data) = 0; + // Adds |entropy| to the random number generator. Returns KM_ERROR_OK on // success and a Keystore ResponseCode or keymaster_error_t on failure. virtual int32_t addRandomNumberGeneratorEntropy(const std::string& entropy) = 0; @@ -112,9 +139,9 @@ class KeystoreClient { // Continues the operation associated with |handle| using the given // |input_parameters| and |input_data|. On success, the - // |num_input_bytes_consumed|, any |output_parameters|, and any |output_data| - // is populated. Returns KM_ERROR_OK on success and a Keystore ResponseCode or - // keymaster_error_t on failure. + // |num_input_bytes_consumed| and any |output_parameters| are populated. Any + // |output_data| will be appended. Returns KM_ERROR_OK on success and a + // Keystore ResponseCode or keymaster_error_t on failure. virtual int32_t updateOperation(keymaster_operation_handle_t handle, const keymaster::AuthorizationSet& input_parameters, const std::string& input_data, size_t* num_input_bytes_consumed, @@ -123,9 +150,9 @@ class KeystoreClient { // Finishes the operation associated with |handle| using the given // |input_parameters| and, if necessary, a |signature_to_verify|. On success, - // any |output_parameters| and final |output_data| are populated. Returns - // KM_ERROR_OK on success and a Keystore ResponseCode or keymaster_error_t on - // failure. + // any |output_parameters| are populated and |output_data| is appended. + // Returns KM_ERROR_OK on success and a Keystore ResponseCode or + // keymaster_error_t on failure. virtual int32_t finishOperation(keymaster_operation_handle_t handle, const keymaster::AuthorizationSet& input_parameters, const std::string& signature_to_verify, diff --git a/keystore/include/keystore/keystore_client_impl.h b/keystore/include/keystore/keystore_client_impl.h index 7ed8443b..21f68f94 100644 --- a/keystore/include/keystore/keystore_client_impl.h +++ b/keystore/include/keystore/keystore_client_impl.h @@ -34,6 +34,15 @@ class KeystoreClientImpl : public KeystoreClient { ~KeystoreClientImpl() override = default; // KeystoreClient methods. + bool encryptWithAuthentication(const std::string& key_name, const std::string& data, + std::string* encrypted_data) override; + bool decryptWithAuthentication(const std::string& key_name, const std::string& encrypted_data, + std::string* data) override; + bool oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name, + const keymaster::AuthorizationSet& input_parameters, + const std::string& input_data, const std::string& signature_to_verify, + keymaster::AuthorizationSet* output_parameters, + std::string* output_data) override; int32_t addRandomNumberGeneratorEntropy(const std::string& entropy) override; int32_t generateKey(const std::string& key_name, const keymaster::AuthorizationSet& key_parameters, @@ -71,12 +80,6 @@ class KeystoreClientImpl : public KeystoreClient { bool listKeys(const std::string& prefix, std::vector<std::string>* key_name_list) override; private: - android::sp<android::IServiceManager> service_manager_; - android::sp<android::IBinder> keystore_binder_; - android::sp<android::IKeystoreService> keystore_; - keymaster_operation_handle_t next_virtual_handle_ = 1; - std::map<keymaster_operation_handle_t, android::sp<android::IBinder>> active_operations_; - // Returns an available virtual operation handle. keymaster_operation_handle_t getNextVirtualHandle(); @@ -84,6 +87,30 @@ class KeystoreClientImpl : public KeystoreClient { // KM_ERROR_OK (not keystore's NO_ERROR). int32_t mapKeystoreError(int32_t keystore_error); + // Creates an encryption key suitable for EncryptWithAuthentication or + // verifies attributes if the key already exists. Returns true on success. + bool createOrVerifyEncryptionKey(const std::string& key_name); + + // Creates an authentication key suitable for EncryptWithAuthentication or + // verifies attributes if the key already exists. Returns true on success. + bool createOrVerifyAuthenticationKey(const std::string& key_name); + + // Verifies attributes of an encryption key suitable for + // EncryptWithAuthentication. Returns true on success and populates |verified| + // with the result of the verification. + bool verifyEncryptionKeyAttributes(const std::string& key_name, bool* verified); + + // Verifies attributes of an authentication key suitable for + // EncryptWithAuthentication. Returns true on success and populates |verified| + // with the result of the verification. + bool verifyAuthenticationKeyAttributes(const std::string& key_name, bool* verified); + + android::sp<android::IServiceManager> service_manager_; + android::sp<android::IBinder> keystore_binder_; + android::sp<android::IKeystoreService> keystore_; + keymaster_operation_handle_t next_virtual_handle_ = 1; + std::map<keymaster_operation_handle_t, android::sp<android::IBinder>> active_operations_; + DISALLOW_COPY_AND_ASSIGN(KeystoreClientImpl); }; diff --git a/keystore/include/keystore/keystore_client_mock.h b/keystore/include/keystore/keystore_client_mock.h index 2bda8444..2d1f4996 100644 --- a/keystore/include/keystore/keystore_client_mock.h +++ b/keystore/include/keystore/keystore_client_mock.h @@ -29,6 +29,17 @@ class KeystoreClientMock : public KeystoreClient { KeystoreClientMock() = default; ~KeystoreClientMock() = default; + MOCK_METHOD3(encryptWithAuthentication, + bool(const std::string& key_name, const std::string& data, + std::string* encrypted_data)); + MOCK_METHOD3(decryptWithAuthentication, + bool(const std::string& key_name, const std::string& encrypted_data, + std::string* data)); + MOCK_METHOD7(oneShotOperation, + bool(keymaster_purpose_t purpose, const std::string& key_name, + const keymaster::AuthorizationSet& input_parameters, + const std::string& input_data, const std::string& signature_to_verify, + keymaster::AuthorizationSet* output_parameters, std::string* output_data)); MOCK_METHOD1(addRandomNumberGeneratorEntropy, int32_t(const std::string& entropy)); MOCK_METHOD4(generateKey, int32_t(const std::string& key_name, diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp index 288d600e..4f4040d3 100644 --- a/keystore/keystore_cli_v2.cpp +++ b/keystore/keystore_cli_v2.cpp @@ -18,6 +18,7 @@ #include <vector> #include "base/command_line.h" +#include "base/files/file_util.h" #include "keymaster/authorization_set.h" #include "keystore/keystore_client_impl.h" @@ -38,7 +39,8 @@ void PrintUsageAndExit() { " delete-all\n" " exists --name=<key_name>\n" " list [--prefix=<key_name_prefix>]\n" - " sign-verify --name=<key_name>\n"); + " sign-verify --name=<key_name>\n" + " [en|de]crypt --name=<key_name> --in=<file> --out=<file>\n"); exit(1); } @@ -46,6 +48,25 @@ std::unique_ptr<KeystoreClient> CreateKeystoreInstance() { return std::unique_ptr<KeystoreClient>(new keystore::KeystoreClientImpl); } +std::string ReadFile(const std::string& filename) { + std::string content; + base::FilePath path(filename); + if (!base::ReadFileToString(path, &content)) { + printf("Failed to read file: %s\n", filename.c_str()); + exit(1); + } + return content; +} + +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) { + printf("Failed to write file: %s\n", filename.c_str()); + exit(1); + } +} + int AddEntropy(const std::string& input) { std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance(); int32_t result = keystore->addRandomNumberGeneratorEntropy(input); @@ -157,6 +178,7 @@ int SignAndVerify(const std::string& name) { printf("Sign: %zu bytes.\n", output_data.size()); // We have a signature, now verify it. std::string signature_to_verify = output_data; + output_data.clear(); result = keystore->beginOperation(KM_PURPOSE_VERIFY, name, sign_params.build(), &output_params, &handle); if (result != KM_ERROR_OK) { @@ -183,6 +205,32 @@ int SignAndVerify(const std::string& name) { return 0; } +int Encrypt(const std::string& key_name, const std::string& input_filename, + const std::string& output_filename) { + std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance(); + std::string input = ReadFile(input_filename); + std::string output; + if (!keystore->encryptWithAuthentication(key_name, input, &output)) { + printf("EncryptWithAuthentication failed.\n"); + return 1; + } + WriteFile(output_filename, output); + return 0; +} + +int Decrypt(const std::string& key_name, const std::string& input_filename, + const std::string& output_filename) { + std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance(); + std::string input = ReadFile(input_filename); + std::string output; + if (!keystore->decryptWithAuthentication(key_name, input, &output)) { + printf("DecryptWithAuthentication failed.\n"); + return 1; + } + WriteFile(output_filename, output); + return 0; +} + } // namespace int main(int argc, char** argv) { @@ -210,6 +258,14 @@ int main(int argc, char** argv) { return List(command_line->GetSwitchValueASCII("prefix")); } else if (args[0] == "sign-verify") { return SignAndVerify(command_line->GetSwitchValueASCII("name")); + } else if (args[0] == "encrypt") { + return Encrypt(command_line->GetSwitchValueASCII("name"), + command_line->GetSwitchValueASCII("in"), + command_line->GetSwitchValueASCII("out")); + } else if (args[0] == "decrypt") { + return Decrypt(command_line->GetSwitchValueASCII("name"), + command_line->GetSwitchValueASCII("in"), + command_line->GetSwitchValueASCII("out")); } else { PrintUsageAndExit(); } diff --git a/keystore/keystore_client.proto b/keystore/keystore_client.proto new file mode 100644 index 00000000..cd520dc0 --- /dev/null +++ b/keystore/keystore_client.proto @@ -0,0 +1,26 @@ +// Copyright 2015 The Android Open Source Project +// +// 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. + +package keystore; + +option optimize_for = LITE_RUNTIME; + +// Holds encrypted, authenticated data. +message EncryptedData { + // The initialization vector used during encryption. + optional bytes init_vector = 1; + // MAC of (init_vector + encrypted_data). + optional bytes authentication_data = 2; + optional bytes encrypted_data = 3; +} diff --git a/keystore/keystore_client_impl.cpp b/keystore/keystore_client_impl.cpp index deaa6153..d4e784f2 100644 --- a/keystore/keystore_client_impl.cpp +++ b/keystore/keystore_client_impl.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#define LOG_TAG "keystore_client" + #include "keystore/keystore_client_impl.h" #include <string> @@ -22,20 +24,29 @@ #include "binder/IServiceManager.h" #include "keystore/IKeystoreService.h" #include "keystore/keystore.h" +#include "log/log.h" #include "utils/String16.h" #include "utils/String8.h" +#include "keystore_client.pb.h" + using android::ExportResult; using android::KeyCharacteristics; using android::KeymasterArguments; using android::OperationResult; using android::String16; using keymaster::AuthorizationSet; +using keymaster::AuthorizationSetBuilder; namespace { // Use the UID of the current process. const int kDefaultUID = -1; +const char kEncryptSuffix[] = "_ENC"; +const char kAuthenticateSuffix[] = "_AUTH"; +const uint32_t kAESKeySize = 256; // bits +const uint32_t kHMACKeySize = 256; // bits +const uint32_t kHMACOutputSize = 256; // bits const uint8_t* StringAsByteArray(const std::string& s) { return reinterpret_cast<const uint8_t*>(s.data()); @@ -55,6 +66,126 @@ KeystoreClientImpl::KeystoreClientImpl() { keystore_ = android::interface_cast<android::IKeystoreService>(keystore_binder_); } +bool KeystoreClientImpl::encryptWithAuthentication(const std::string& key_name, + const std::string& data, + std::string* encrypted_data) { + // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random + // IV. The authentication algorithm is HMAC-SHA256 and is computed over the + // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM + // because hardware support for GCM is not mandatory for all Brillo devices. + std::string encryption_key_name = key_name + kEncryptSuffix; + if (!createOrVerifyEncryptionKey(encryption_key_name)) { + return false; + } + std::string authentication_key_name = key_name + kAuthenticateSuffix; + if (!createOrVerifyAuthenticationKey(authentication_key_name)) { + return false; + } + AuthorizationSetBuilder encrypt_params; + encrypt_params.Padding(KM_PAD_PKCS7); + encrypt_params.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC); + AuthorizationSet output_params; + std::string raw_encrypted_data; + if (!oneShotOperation(KM_PURPOSE_ENCRYPT, encryption_key_name, encrypt_params.build(), data, + std::string(), /* signature_to_verify */ + &output_params, &raw_encrypted_data)) { + ALOGE("Encrypt: AES operation failed."); + return false; + } + keymaster_blob_t init_vector_blob; + if (!output_params.GetTagValue(keymaster::TAG_NONCE, &init_vector_blob)) { + ALOGE("Encrypt: Missing initialization vector."); + return false; + } + std::string init_vector = + ByteArrayAsString(init_vector_blob.data, init_vector_blob.data_length); + + AuthorizationSetBuilder authenticate_params; + authenticate_params.Digest(KM_DIGEST_SHA_2_256); + authenticate_params.Authorization(keymaster::TAG_MAC_LENGTH, kHMACOutputSize); + std::string raw_authentication_data; + if (!oneShotOperation(KM_PURPOSE_SIGN, authentication_key_name, authenticate_params.build(), + init_vector + raw_encrypted_data, std::string(), /* signature_to_verify */ + &output_params, &raw_authentication_data)) { + ALOGE("Encrypt: HMAC operation failed."); + return false; + } + EncryptedData protobuf; + protobuf.set_init_vector(init_vector); + protobuf.set_authentication_data(raw_authentication_data); + protobuf.set_encrypted_data(raw_encrypted_data); + if (!protobuf.SerializeToString(encrypted_data)) { + ALOGE("Encrypt: Failed to serialize EncryptedData protobuf."); + return false; + } + return true; +} + +bool KeystoreClientImpl::decryptWithAuthentication(const std::string& key_name, + const std::string& encrypted_data, + std::string* data) { + EncryptedData protobuf; + if (!protobuf.ParseFromString(encrypted_data)) { + ALOGE("Decrypt: Failed to parse EncryptedData protobuf."); + } + // Verify authentication before attempting decryption. + std::string authentication_key_name = key_name + kAuthenticateSuffix; + AuthorizationSetBuilder authenticate_params; + authenticate_params.Digest(KM_DIGEST_SHA_2_256); + AuthorizationSet output_params; + std::string output_data; + if (!oneShotOperation(KM_PURPOSE_VERIFY, authentication_key_name, authenticate_params.build(), + protobuf.init_vector() + protobuf.encrypted_data(), + protobuf.authentication_data(), &output_params, &output_data)) { + ALOGE("Decrypt: HMAC operation failed."); + return false; + } + std::string encryption_key_name = key_name + kEncryptSuffix; + AuthorizationSetBuilder encrypt_params; + encrypt_params.Padding(KM_PAD_PKCS7); + encrypt_params.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC); + encrypt_params.Authorization(keymaster::TAG_NONCE, protobuf.init_vector().data(), + protobuf.init_vector().size()); + if (!oneShotOperation(KM_PURPOSE_DECRYPT, encryption_key_name, encrypt_params.build(), + protobuf.encrypted_data(), std::string(), /* signature_to_verify */ + &output_params, data)) { + ALOGE("Decrypt: AES operation failed."); + return false; + } + return true; +} + +bool KeystoreClientImpl::oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name, + const keymaster::AuthorizationSet& input_parameters, + const std::string& input_data, + const std::string& signature_to_verify, + keymaster::AuthorizationSet* output_parameters, + std::string* output_data) { + keymaster_operation_handle_t handle; + int32_t result = + beginOperation(purpose, key_name, input_parameters, output_parameters, &handle); + if (result != KM_ERROR_OK) { + ALOGE("BeginOperation failed: %d", result); + return false; + } + AuthorizationSet empty_params; + size_t num_input_bytes_consumed; + AuthorizationSet ignored_params; + result = updateOperation(handle, empty_params, input_data, &num_input_bytes_consumed, + &ignored_params, output_data); + if (result != KM_ERROR_OK) { + ALOGE("UpdateOperation failed: %d", result); + return false; + } + result = + finishOperation(handle, empty_params, signature_to_verify, &ignored_params, output_data); + if (result != KM_ERROR_OK) { + ALOGE("FinishOperation failed: %d", result); + return false; + } + return true; +} + int32_t KeystoreClientImpl::addRandomNumberGeneratorEntropy(const std::string& entropy) { return mapKeystoreError(keystore_->addRngEntropy(StringAsByteArray(entropy), entropy.size())); } @@ -173,7 +304,7 @@ int32_t KeystoreClientImpl::updateOperation(keymaster_operation_handle_t handle, output_parameters->Reinitialize(&*result.outParams.params.begin(), result.outParams.params.size()); } - *output_data = ByteArrayAsString(result.data.get(), result.dataLength); + output_data->append(ByteArrayAsString(result.data.get(), result.dataLength)); } return error_code; } @@ -198,7 +329,7 @@ int32_t KeystoreClientImpl::finishOperation(keymaster_operation_handle_t handle, output_parameters->Reinitialize(&*result.outParams.params.begin(), result.outParams.params.size()); } - *output_data = ByteArrayAsString(result.data.get(), result.dataLength); + output_data->append(ByteArrayAsString(result.data.get(), result.dataLength)); active_operations_.erase(handle); } return error_code; @@ -248,4 +379,171 @@ int32_t KeystoreClientImpl::mapKeystoreError(int32_t keystore_error) { return keystore_error; } +bool KeystoreClientImpl::createOrVerifyEncryptionKey(const std::string& key_name) { + bool key_exists = doesKeyExist(key_name); + if (key_exists) { + bool verified = false; + if (!verifyEncryptionKeyAttributes(key_name, &verified)) { + return false; + } + if (!verified) { + int32_t result = deleteKey(key_name); + if (result != KM_ERROR_OK) { + ALOGE("Failed to delete invalid encryption key: %d", result); + return false; + } + key_exists = false; + } + } + if (!key_exists) { + AuthorizationSetBuilder key_parameters; + key_parameters.AesEncryptionKey(kAESKeySize) + .Padding(KM_PAD_PKCS7) + .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC) + .Authorization(keymaster::TAG_NO_AUTH_REQUIRED); + AuthorizationSet hardware_enforced_characteristics; + AuthorizationSet software_enforced_characteristics; + int32_t result = + generateKey(key_name, key_parameters.build(), &hardware_enforced_characteristics, + &software_enforced_characteristics); + if (result != KM_ERROR_OK) { + ALOGE("Failed to generate encryption key: %d", result); + return false; + } + if (hardware_enforced_characteristics.size() == 0) { + ALOGW("WARNING: Encryption key is not hardware-backed."); + } + } + return true; +} + +bool KeystoreClientImpl::createOrVerifyAuthenticationKey(const std::string& key_name) { + bool key_exists = doesKeyExist(key_name); + if (key_exists) { + bool verified = false; + if (!verifyAuthenticationKeyAttributes(key_name, &verified)) { + return false; + } + if (!verified) { + int32_t result = deleteKey(key_name); + if (result != KM_ERROR_OK) { + ALOGE("Failed to delete invalid authentication key: %d", result); + return false; + } + key_exists = false; + } + } + if (!key_exists) { + AuthorizationSetBuilder key_parameters; + key_parameters.HmacKey(kHMACKeySize) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(keymaster::TAG_MIN_MAC_LENGTH, kHMACOutputSize) + .Authorization(keymaster::TAG_NO_AUTH_REQUIRED); + AuthorizationSet hardware_enforced_characteristics; + AuthorizationSet software_enforced_characteristics; + int32_t result = + generateKey(key_name, key_parameters.build(), &hardware_enforced_characteristics, + &software_enforced_characteristics); + if (result != KM_ERROR_OK) { + ALOGE("Failed to generate authentication key: %d", result); + return false; + } + if (hardware_enforced_characteristics.size() == 0) { + ALOGW("WARNING: Authentication key is not hardware-backed."); + } + } + return true; +} + +bool KeystoreClientImpl::verifyEncryptionKeyAttributes(const std::string& key_name, + bool* verified) { + AuthorizationSet hardware_enforced_characteristics; + AuthorizationSet software_enforced_characteristics; + int32_t result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics, + &software_enforced_characteristics); + if (result != KM_ERROR_OK) { + ALOGE("Failed to query encryption key: %d", result); + return false; + } + *verified = true; + keymaster_algorithm_t algorithm = KM_ALGORITHM_RSA; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm)) || + algorithm != KM_ALGORITHM_AES) { + ALOGW("Found encryption key with invalid algorithm."); + *verified = false; + } + uint32_t key_size = 0; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size)) || + key_size != kAESKeySize) { + ALOGW("Found encryption key with invalid size."); + *verified = false; + } + keymaster_block_mode_t block_mode = KM_MODE_ECB; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_BLOCK_MODE, &block_mode) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_BLOCK_MODE, &block_mode)) || + block_mode != KM_MODE_CBC) { + ALOGW("Found encryption key with invalid block mode."); + *verified = false; + } + keymaster_padding_t padding_mode = KM_PAD_NONE; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_PADDING, &padding_mode) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_PADDING, &padding_mode)) || + padding_mode != KM_PAD_PKCS7) { + ALOGW("Found encryption key with invalid padding mode."); + *verified = false; + } + if (hardware_enforced_characteristics.size() == 0) { + ALOGW("WARNING: Encryption key is not hardware-backed."); + } + return true; +} + +bool KeystoreClientImpl::verifyAuthenticationKeyAttributes(const std::string& key_name, + bool* verified) { + AuthorizationSet hardware_enforced_characteristics; + AuthorizationSet software_enforced_characteristics; + int32_t result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics, + &software_enforced_characteristics); + if (result != KM_ERROR_OK) { + ALOGE("Failed to query authentication key: %d", result); + return false; + } + *verified = true; + keymaster_algorithm_t algorithm = KM_ALGORITHM_RSA; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm)) || + algorithm != KM_ALGORITHM_HMAC) { + ALOGW("Found authentication key with invalid algorithm."); + *verified = false; + } + uint32_t key_size = 0; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size)) || + key_size != kHMACKeySize) { + ALOGW("Found authentication key with invalid size."); + *verified = false; + } + uint32_t mac_size = 0; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_MIN_MAC_LENGTH, &mac_size) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_MIN_MAC_LENGTH, + &mac_size)) || + mac_size != kHMACOutputSize) { + ALOGW("Found authentication key with invalid minimum mac size."); + *verified = false; + } + keymaster_digest_t digest = KM_DIGEST_NONE; + if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_DIGEST, &digest) && + !software_enforced_characteristics.GetTagValue(keymaster::TAG_DIGEST, &digest)) || + digest != KM_DIGEST_SHA_2_256) { + ALOGW("Found authentication key with invalid digest list."); + *verified = false; + } + if (hardware_enforced_characteristics.size() == 0) { + ALOGW("WARNING: Authentication key is not hardware-backed."); + } + return true; +} + } // namespace keystore |