diff options
author | Darren Krahn <dkrahn@google.com> | 2015-09-28 08:51:18 -0700 |
---|---|---|
committer | Gaurav Shah <gauravsh@google.com> | 2015-11-02 09:12:59 -0800 |
commit | 251cb28132e456f81374c8f8a983a5a9ad9aaee8 (patch) | |
tree | 2a6e7b1e61c7ebd24dc805697d70b4ed2062908e /keystore/keystore_client_impl.cpp | |
parent | 69a3dbc2bbbe0b304eb91376ff7f79c8bde995a1 (diff) | |
download | security-251cb28132e456f81374c8f8a983a5a9ad9aaee8.tar.gz |
Add encryption convenience methods to KeystoreClient.
This Cl adds authenticated encryption and decryption methods which
require minimal inputs. These methods are suitable for encrypting local
state on brillo.
BUG: 23528174
TEST=manual using the keystore_cli_v2 tool
Change-Id: I41abcd77452e86b1eb7373f9db95b645100e2f0f
Diffstat (limited to 'keystore/keystore_client_impl.cpp')
-rw-r--r-- | keystore/keystore_client_impl.cpp | 302 |
1 files changed, 300 insertions, 2 deletions
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 |