summaryrefslogtreecommitdiff
path: root/keystore/keystore_client_impl.cpp
diff options
context:
space:
mode:
authorDarren Krahn <dkrahn@google.com>2015-09-28 08:51:18 -0700
committerGaurav Shah <gauravsh@google.com>2015-11-02 09:12:59 -0800
commit251cb28132e456f81374c8f8a983a5a9ad9aaee8 (patch)
tree2a6e7b1e61c7ebd24dc805697d70b4ed2062908e /keystore/keystore_client_impl.cpp
parent69a3dbc2bbbe0b304eb91376ff7f79c8bde995a1 (diff)
downloadsecurity-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.cpp302
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