| /* Copyright 2017 The Android Open Source Project |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
| |
| #include "keystore_backend_binder.h" |
| |
| #include <android-base/logging.h> |
| #include <android/security/keystore/IKeystoreService.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/ProcessState.h> |
| #include <keystore/KeyCharacteristics.h> |
| #include <keystore/KeymasterArguments.h> |
| #include <keystore/KeymasterBlob.h> |
| #include <keystore/KeystoreResponse.h> |
| #include <keystore/OperationResult.h> |
| #include <keystore/keymaster_types.h> |
| #include <keystore/keystore.h> |
| #include <keystore/keystore_hidl_support.h> |
| #include <keystore/keystore_promises.h> |
| #include <keystore/keystore_return_types.h> |
| |
| #include <future> |
| #include <thread> |
| |
| using android::security::keystore::IKeystoreService; |
| using namespace android; |
| using keystore::hidl_vec; |
| |
| using android::hardware::keymaster::V4_0::Algorithm; |
| using android::hardware::keymaster::V4_0::authorizationValue; |
| using android::hardware::keymaster::V4_0::Digest; |
| using android::hardware::keymaster::V4_0::KeyFormat; |
| using android::hardware::keymaster::V4_0::KeyParameter; |
| using android::hardware::keymaster::V4_0::KeyPurpose; |
| using android::hardware::keymaster::V4_0::NullOr; |
| using android::hardware::keymaster::V4_0::PaddingMode; |
| using android::hardware::keymaster::V4_0::TAG_ALGORITHM; |
| using android::hardware::keymaster::V4_0::TAG_DIGEST; |
| using android::hardware::keymaster::V4_0::TAG_PADDING; |
| using android::security::keymaster::ExportResult; |
| using android::security::keymaster::KeyCharacteristics; |
| using android::security::keymaster::KeymasterArguments; |
| using android::security::keymaster::KeymasterBlob; |
| using android::security::keymaster::OperationResult; |
| |
| using KSReturn = keystore::KeyStoreNativeReturnCode; |
| |
| namespace { |
| const char keystore_service_name[] = "android.security.keystore"; |
| constexpr int32_t UID_SELF = -1; |
| |
| using keystore::KeyCharacteristicsPromise; |
| using keystore::KeystoreExportPromise; |
| using keystore::KeystoreResponsePromise; |
| using keystore::OperationResultPromise; |
| |
| } // namespace |
| |
| #define AT __func__ << ":" << __LINE__ << " " |
| |
| static NullOr<const Algorithm&> getKeyAlgoritmFromKeyCharacteristics( |
| const ::android::security::keymaster::KeyCharacteristics& characteristics) { |
| for (const auto& param : characteristics.hardwareEnforced.getParameters()) { |
| auto algo = authorizationValue(TAG_ALGORITHM, param); |
| if (algo.isOk()) return algo; |
| } |
| for (const auto& param : characteristics.softwareEnforced.getParameters()) { |
| auto algo = authorizationValue(TAG_ALGORITHM, param); |
| if (algo.isOk()) return algo; |
| } |
| return {}; |
| } |
| |
| KeystoreBackendBinder::KeystoreBackendBinder() { |
| android::ProcessState::self()->startThreadPool(); |
| } |
| |
| int32_t KeystoreBackendBinder::sign(const char* key_id, const uint8_t* in, size_t len, |
| uint8_t** reply, size_t* reply_len) { |
| sp<IServiceManager> sm = defaultServiceManager(); |
| sp<IBinder> binder = sm->getService(String16(keystore_service_name)); |
| sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder); |
| |
| if (service == nullptr) { |
| LOG(ERROR) << AT << "could not contact keystore"; |
| return -1; |
| } |
| |
| String16 key_name16(key_id); |
| int32_t error_code; |
| android::sp<KeyCharacteristicsPromise> kc_promise(new KeyCharacteristicsPromise); |
| auto kc_future = kc_promise->get_future(); |
| auto binder_result = service->getKeyCharacteristics(kc_promise, key_name16, KeymasterBlob(), |
| KeymasterBlob(), UID_SELF, &error_code); |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| if (!KSReturn(error_code).isOk()) { |
| LOG(ERROR) << AT << "getKeyCharacteristics failed: " << error_code; |
| return -1; |
| } |
| |
| auto [km_response, characteristics] = kc_future.get(); |
| |
| if (!KSReturn(km_response.response_code()).isOk()) { |
| LOG(ERROR) << AT << "getKeyCharacteristics failed: " << km_response.response_code(); |
| return -1; |
| } |
| |
| auto algorithm = getKeyAlgoritmFromKeyCharacteristics(characteristics); |
| if (!algorithm.isOk()) { |
| LOG(ERROR) << AT << "could not get algorithm from key characteristics"; |
| return -1; |
| } |
| |
| hidl_vec<KeyParameter> params(3); |
| params[0] = Authorization(TAG_DIGEST, Digest::NONE); |
| params[1] = Authorization(TAG_PADDING, PaddingMode::NONE); |
| params[2] = Authorization(TAG_ALGORITHM, algorithm.value()); |
| |
| android::sp<android::IBinder> token(new android::BBinder); |
| sp<OperationResultPromise> promise(new OperationResultPromise()); |
| auto future = promise->get_future(); |
| binder_result = service->begin(promise, token, key_name16, (int)KeyPurpose::SIGN, |
| true /*pruneable*/, KeymasterArguments(params), |
| std::vector<uint8_t>() /* entropy */, UID_SELF, &error_code); |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| |
| keystore::KeyStoreNativeReturnCode rc(error_code); |
| if (!rc.isOk()) { |
| LOG(ERROR) << AT << "Keystore begin returned: " << error_code; |
| return -1; |
| } |
| OperationResult result = future.get(); |
| |
| if (!result.resultCode.isOk()) { |
| LOG(ERROR) << AT << "begin failed: " << result.resultCode; |
| return -1; |
| } |
| auto handle = std::move(result.token); |
| |
| do { |
| future = {}; |
| promise = new OperationResultPromise(); |
| future = promise->get_future(); |
| binder_result = service->update(promise, handle, KeymasterArguments(params), |
| std::vector<uint8_t>(in, in + len), &error_code); |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| |
| rc = keystore::KeyStoreNativeReturnCode(error_code); |
| if (!rc.isOk()) { |
| LOG(ERROR) << AT << "Keystore update returned: " << error_code; |
| return -1; |
| } |
| result = future.get(); |
| |
| if (!result.resultCode.isOk()) { |
| LOG(ERROR) << AT << "update failed: " << result.resultCode; |
| return -1; |
| } |
| |
| if (result.inputConsumed > len) { |
| LOG(ERROR) << AT << "update consumed more data than provided"; |
| sp<KeystoreResponsePromise> abortPromise(new KeystoreResponsePromise); |
| auto abortFuture = abortPromise->get_future(); |
| binder_result = service->abort(abortPromise, handle, &error_code); |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| // This is mainly for logging since we already failed. |
| // But if abort returned OK we have to wait untill abort calls the callback |
| // hence the call to abortFuture.get(). |
| if (!KSReturn(error_code).isOk()) { |
| LOG(ERROR) << AT << "abort failed: " << error_code; |
| } else if (!(rc = KSReturn(abortFuture.get().response_code())).isOk()) { |
| LOG(ERROR) << AT << "abort failed: " << rc; |
| } |
| return -1; |
| } |
| len -= result.inputConsumed; |
| in += result.inputConsumed; |
| } while (len > 0); |
| |
| future = {}; |
| promise = new OperationResultPromise(); |
| future = promise->get_future(); |
| |
| binder_result = service->finish( |
| promise, handle, KeymasterArguments(params), std::vector<uint8_t>() /* input */, |
| std::vector<uint8_t>() /* signature */, std::vector<uint8_t>() /* entropy */, &error_code); |
| |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| |
| rc = keystore::KeyStoreNativeReturnCode(error_code); |
| if (!rc.isOk()) { |
| LOG(ERROR) << AT << "Keystore finish returned: " << error_code; |
| return -1; |
| } |
| result = future.get(); |
| |
| if (!result.resultCode.isOk()) { |
| LOG(ERROR) << AT << "finish failed: " << result.resultCode; |
| return -1; |
| } |
| |
| hidl_vec<uint8_t> reply_hidl(result.data); |
| if (reply_len) { |
| *reply_len = reply_hidl.size(); |
| } |
| if (reply) { |
| *reply = reply_hidl.releaseData(); |
| } |
| return 0; |
| } |
| |
| int32_t KeystoreBackendBinder::get_pubkey(const char* key_id, uint8_t** pubkey, |
| size_t* pubkey_len) { |
| sp<IServiceManager> sm = defaultServiceManager(); |
| sp<IBinder> binder = sm->getService(String16(keystore_service_name)); |
| sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder); |
| |
| if (service == nullptr) { |
| LOG(ERROR) << AT << "could not contact keystore"; |
| return -1; |
| } |
| |
| int32_t error_code; |
| android::sp<KeystoreExportPromise> promise(new KeystoreExportPromise); |
| auto future = promise->get_future(); |
| auto binder_result = service->exportKey( |
| promise, String16(key_id), static_cast<int32_t>(KeyFormat::X509), |
| KeymasterBlob() /* clientId */, KeymasterBlob() /* appData */, UID_SELF, &error_code); |
| if (!binder_result.isOk()) { |
| LOG(ERROR) << AT << "communication error while calling keystore"; |
| return -1; |
| } |
| |
| KSReturn rc(error_code); |
| if (!rc.isOk()) { |
| LOG(ERROR) << AT << "exportKey failed: " << error_code; |
| return -1; |
| } |
| |
| auto export_result = future.get(); |
| if (!export_result.resultCode.isOk()) { |
| LOG(ERROR) << AT << "exportKey failed: " << export_result.resultCode; |
| return -1; |
| } |
| |
| if (pubkey_len) { |
| *pubkey_len = export_result.exportData.size(); |
| } |
| if (pubkey) { |
| *pubkey = export_result.exportData.releaseData(); |
| } |
| return 0; |
| } |