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
diff --git a/keystore/keystore_client_impl.cpp b/keystore/keystore_client_impl.cpp
index deaa615..d4e784f 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 @@
     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 @@
             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 @@
             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 @@
     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