diff options
author | David Zeuthen <zeuthen@google.com> | 2020-02-11 22:04:24 -0500 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2020-02-14 13:48:55 -0500 |
commit | 81603155a9582941cf558e840ffff0c90edaf330 (patch) | |
tree | 8951666994a7169ddc89e4a4c2a81207db974d89 /identity | |
parent | 25c2de29b9d41c6e1e2c12ba18cb0680023b9092 (diff) | |
download | platform_hardware_interfaces-81603155a9582941cf558e840ffff0c90edaf330.tar.gz platform_hardware_interfaces-81603155a9582941cf558e840ffff0c90edaf330.tar.bz2 platform_hardware_interfaces-81603155a9582941cf558e840ffff0c90edaf330.zip |
Port IdentityCredential HAL to AIDL.
This includes add a partial types-only HAL for KeyMaster for
HardwareAuthToken.
Bug: 111446262
Test: atest android.security.identity.cts
Test: VtsHalIdentityTargetTest
Test: android.hardware.identity-support-lib-test
Change-Id: I7a6254d33200bfd62269aed1957cbb2a84b16272
Diffstat (limited to 'identity')
37 files changed, 1502 insertions, 1579 deletions
diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp deleted file mode 100644 index e0a6332f89..0000000000 --- a/identity/1.0/Android.bp +++ /dev/null @@ -1,20 +0,0 @@ -// This file is autogenerated by hidl-gen -Landroidbp. - -hidl_interface { - name: "android.hardware.identity@1.0", - root: "android.hardware", - vndk: { - enabled: true, - }, - srcs: [ - "types.hal", - "IIdentityCredential.hal", - "IIdentityCredentialStore.hal", - "IWritableIdentityCredential.hal", - ], - interfaces: [ - "android.hardware.keymaster@4.0", - "android.hidl.base@1.0", - ], - gen_java: false, -} diff --git a/identity/1.0/default/Android.bp b/identity/1.0/default/Android.bp deleted file mode 100644 index d2b29660d3..0000000000 --- a/identity/1.0/default/Android.bp +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (C) 2019 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. -// - -cc_binary { - name: "android.hardware.identity@1.0-service.example", - init_rc: ["android.hardware.identity@1.0-service.example.rc"], - vendor: true, - relative_install_path: "hw", - cflags: [ - "-Wall", - "-Wextra", - ], - srcs: [ - "service.cpp", - "IdentityCredential.cpp", - "IdentityCredentialStore.cpp", - "WritableIdentityCredential.cpp", - ], - shared_libs: [ - "android.hardware.identity@1.0", - "android.hardware.identity-support-lib", - "android.hardware.keymaster@4.0", - "libcppbor", - "libcrypto", - "libbase", - "libhidlbase", - "liblog", - "libutils", - ], -} diff --git a/identity/1.0/default/IdentityCredential.h b/identity/1.0/default/IdentityCredential.h deleted file mode 100644 index eb8787b872..0000000000 --- a/identity/1.0/default/IdentityCredential.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2019, 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. - */ - -#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H -#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H - -#include <android/hardware/identity/1.0/IIdentityCredential.h> - -#include <android/hardware/identity/support/IdentityCredentialSupport.h> - -#include <map> -#include <set> -#include <string> -#include <vector> - -#include <cppbor/cppbor.h> - -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -using ::std::map; -using ::std::string; -using ::std::vector; - -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::identity::V1_0::IIdentityCredential; -using ::android::hardware::identity::V1_0::Result; -using ::android::hardware::identity::V1_0::ResultCode; -using ::android::hardware::identity::V1_0::SecureAccessControlProfile; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; - -using MapStringToVectorOfStrings = map<string, vector<string>>; - -class IdentityCredential : public IIdentityCredential { - public: - IdentityCredential(const hidl_vec<uint8_t>& credentialData) - : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {} - - // Parses and decrypts credentialData_, return false on failure. Must be - // called right after construction. - ResultCode initialize(); - - // Methods from ::android::hardware::identity::IIdentityCredential follow. - - Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override; - Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override; - - Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey, - setReaderEphemeralPublicKey_cb _hidl_cb) override; - - Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override; - - Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles, - const HardwareAuthToken& authToken, - const hidl_vec<uint8_t>& itemsRequest, - const hidl_vec<uint8_t>& sessionTranscript, - const hidl_vec<uint8_t>& readerSignature, - const hidl_vec<uint16_t>& requestCounts, - startRetrieval_cb _hidl_cb) override; - Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name, - uint32_t entrySize, - const hidl_vec<uint16_t>& accessControlProfileIds, - startRetrieveEntryValue_cb _hidl_cb) override; - Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent, - retrieveEntryValue_cb _hidl_cb) override; - Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob, - finishRetrieval_cb _hidl_cb) override; - - Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override; - - private: - // Set by constructor - vector<uint8_t> credentialData_; - int numStartRetrievalCalls_; - - // Set by initialize() - string docType_; - bool testCredential_; - vector<uint8_t> storageKey_; - vector<uint8_t> credentialPrivKey_; - - // Set by createEphemeralKeyPair() - vector<uint8_t> ephemeralPublicKey_; - - // Set by setReaderEphemeralPublicKey() - vector<uint8_t> readerPublicKey_; - - // Set by createAuthChallenge() - uint64_t authChallenge_; - - // Set at startRetrieval() time. - map<uint16_t, ResultCode> profileIdToAccessCheckResult_; - vector<uint8_t> sessionTranscript_; - std::unique_ptr<cppbor::Item> sessionTranscriptItem_; - vector<uint8_t> itemsRequest_; - vector<uint16_t> requestCountsRemaining_; - MapStringToVectorOfStrings requestedNameSpacesAndNames_; - cppbor::Map deviceNameSpacesMap_; - cppbor::Map currentNameSpaceDeviceNameSpacesMap_; - - // Set at startRetrieveEntryValue() time. - string currentNameSpace_; - string currentName_; - size_t entryRemainingBytes_; - vector<uint8_t> entryValue_; - vector<uint8_t> entryAdditionalData_; -}; - -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H diff --git a/identity/1.0/default/IdentityCredentialStore.cpp b/identity/1.0/default/IdentityCredentialStore.cpp deleted file mode 100644 index 9eb1e70751..0000000000 --- a/identity/1.0/default/IdentityCredentialStore.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2019, 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. - */ - -#define LOG_TAG "IdentityCredentialStore" - -#include "IdentityCredentialStore.h" -#include "IdentityCredential.h" -#include "WritableIdentityCredential.h" - -#include <android-base/logging.h> - -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -// Methods from ::android::hardware::identity::IIdentityCredentialStore follow. - -Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) { - _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google", - kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */); - return Void(); -} - -Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType, - bool testCredential, - createCredential_cb _hidl_cb) { - auto writable_credential = new WritableIdentityCredential(docType, testCredential); - if (!writable_credential->initialize()) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error initializing WritableIdentityCredential"), - writable_credential); - return Void(); - } - _hidl_cb(support::resultOK(), writable_credential); - return Void(); -} - -Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData, - getCredential_cb _hidl_cb) { - auto credential = new IdentityCredential(credentialData); - // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now. - auto ret = credential->initialize(); - if (ret != ResultCode::OK) { - _hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential); - return Void(); - } - _hidl_cb(support::resultOK(), credential); - return Void(); -} - -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android diff --git a/identity/1.0/default/IdentityCredentialStore.h b/identity/1.0/default/IdentityCredentialStore.h deleted file mode 100644 index ad7536043f..0000000000 --- a/identity/1.0/default/IdentityCredentialStore.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019, 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. - */ - -#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H -#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H - -#include <android/hardware/identity/1.0/IIdentityCredentialStore.h> - -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::identity::V1_0::IIdentityCredentialStore; -using ::android::hardware::identity::V1_0::Result; -using ::android::hardware::identity::V1_0::ResultCode; - -class IdentityCredentialStore : public IIdentityCredentialStore { - public: - IdentityCredentialStore() {} - - // The GCM chunk size used by this implementation is 64 KiB. - static constexpr size_t kGcmChunkSize = 64 * 1024; - - // Methods from ::android::hardware::identity::IIdentityCredentialStore follow. - Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override; - Return<void> createCredential(const hidl_string& docType, bool testCredential, - createCredential_cb _hidl_cb) override; - Return<void> getCredential(const hidl_vec<uint8_t>& credentialData, - getCredential_cb _hidl_cb) override; -}; - -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h deleted file mode 100644 index b1deb16750..0000000000 --- a/identity/1.0/default/WritableIdentityCredential.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2019, 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. - */ - -#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H -#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H - -#include <android/hardware/identity/1.0/IWritableIdentityCredential.h> - -#include <android/hardware/identity/support/IdentityCredentialSupport.h> - -#include <cppbor.h> - -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -using ::std::string; -using ::std::vector; - -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::identity::V1_0::IWritableIdentityCredential; -using ::android::hardware::identity::V1_0::Result; -using ::android::hardware::identity::V1_0::ResultCode; -using ::android::hardware::identity::V1_0::SecureAccessControlProfile; - -class WritableIdentityCredential : public IWritableIdentityCredential { - public: - WritableIdentityCredential(const hidl_string& docType, bool testCredential) - : docType_(docType), testCredential_(testCredential) {} - - // Creates the Credential Key. Returns false on failure. Must be called - // right after construction. - bool initialize(); - - // Methods from ::android::hardware::identity::IWritableIdentityCredential - // follow. - Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationApplicationId, - const hidl_vec<uint8_t>& attestationChallenge, - getAttestationCertificate_cb _hidl_cb) override; - - Return<void> startPersonalization(uint16_t accessControlProfileCount, - const hidl_vec<uint16_t>& entryCounts, - startPersonalization_cb _hidl_cb) override; - - Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate, - bool userAuthenticationRequired, uint64_t timeoutMillis, - uint64_t secureUserId, - addAccessControlProfile_cb _hidl_cb) override; - - Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds, - const hidl_string& nameSpace, const hidl_string& name, - uint32_t entrySize, beginAddEntry_cb _hidl_cb) override; - - Return<void> addEntryValue(const hidl_vec<uint8_t>& content, - addEntryValue_cb _hidl_cb) override; - - Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override; - - private: - string docType_; - bool testCredential_; - - // These are set in initialize(). - vector<uint8_t> storageKey_; - vector<uint8_t> credentialPrivKey_; - vector<uint8_t> credentialPubKey_; - - // These fields are initialized during startPersonalization() - size_t numAccessControlProfileRemaining_; - vector<uint16_t> remainingEntryCounts_; - cppbor::Array signedDataAccessControlProfiles_; - cppbor::Map signedDataNamespaces_; - cppbor::Array signedDataCurrentNamespace_; - - // These fields are initialized during beginAddEntry() - size_t entryRemainingBytes_; - vector<uint8_t> entryAdditionalData_; - string entryNameSpace_; - string entryName_; - vector<uint16_t> entryAccessControlProfileIds_; - vector<uint8_t> entryBytes_; -}; - -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H diff --git a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc deleted file mode 100644 index 1eb7319e14..0000000000 --- a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc +++ /dev/null @@ -1,3 +0,0 @@ -service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example - class hal - user nobody diff --git a/identity/1.0/default/service.cpp b/identity/1.0/default/service.cpp deleted file mode 100644 index 839e8030ea..0000000000 --- a/identity/1.0/default/service.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2019, 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. - */ - -#define LOG_TAG "android.hardware.identity@1.0-service" - -#include <android-base/logging.h> -#include <hidl/HidlTransportSupport.h> - -#include "IdentityCredentialStore.h" - -using android::hardware::joinRpcThreadpool; -using android::hardware::identity::implementation::IdentityCredentialStore; - -int main(int /* argc */, char* argv[]) { - ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/); - - ::android::base::InitLogging(argv, &android::base::StderrLogger); - - auto identity_store = new IdentityCredentialStore(); - auto status = identity_store->registerAsService(); - if (status != android::OK) { - LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status - << ")"; - } - joinRpcThreadpool(); - return -1; // Should never get here. -} diff --git a/identity/1.0/types.hal b/identity/1.0/types.hal deleted file mode 100644 index 5aedfea4dc..0000000000 --- a/identity/1.0/types.hal +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2018 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 android.hardware.identity@1.0; - -/** - * The ResultCode enumeration is used to convey the status of an operation. - */ -enum ResultCode : int32_t { - /** - * Success. - */ - OK = 0, - - /** - * The operation failed. This is used as a generic catch-all for errors that don't belong - * in other categories, including memory/resource allocation failures and I/O errors. - */ - FAILED = 1, - - /** - * The passed data was invalid. This is a generic catch all for errors that don't belong - * in other categories related to parameter validation. - */ - INVALID_DATA = 2, - - /** - * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid. - */ - INVALID_AUTH_TOKEN = 3, - - /** - * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet - * the requirements described in the documentation for that method. - */ - INVALID_ITEMS_REQUEST_MESSAGE = 4, - - /** - * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid, - * doesn't contain an embedded certificate chain, or the signature failed to - * validate. - */ - READER_SIGNATURE_CHECK_FAILED = 5, - - /** - * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public - * key returned by createEphemeralPublicKey(). - */ - EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6, - - /** - * An access condition related to user authentication was not satisfied. - */ - USER_AUTHENTICATION_FAILED = 7, - - /** - * An access condition related to reader authentication was not satisfied. - */ - READER_AUTHENTICATION_FAILED = 8, - - /** - * The request data element has no access control profiles associated so it cannot be accessed. - */ - NO_ACCESS_CONTROL_PROFILES = 9, - - /** - * The requested data element is not in the provided non-empty itemsRequest message. - */ - NOT_IN_REQUEST_MESSAGE = 10, - - /** - * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript. - */ - SESSION_TRANSCRIPT_MISMATCH = 11, -}; - -/** - * A result has a ResultCode and corresponding textual message. - */ -struct Result { - /** - * The result code. - * - * Implementations must not use values not defined in the ResultCode enumeration. - */ - ResultCode code; - - /** - * A human-readable message in English conveying more detail about a failure. - * - * If code is ResultCode::OK this field must be set to the empty string. - */ - string message; -}; - -struct SecureAccessControlProfile { - /** - * id is a numeric identifier that must be unique within the context of a Credential and may be - * used to reference the profile. - */ - uint16_t id; - - /** - * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain - * of certificates) that must be used to authenticate requests. For details about how - * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval. - */ - vec<uint8_t> readerCertificate; - - /** - * if true, the user is required to authenticate to allow requests. Required authentication - * fressness is specified by timeout below. - * - */ - bool userAuthenticationRequired; - - /** - * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see - * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired - * is true and timout is zero then authentication is required for each reader session. - * - * If userAuthenticationRequired is false, timeout must be zero. - */ - uint64_t timeoutMillis; - - /** - * secureUserId must be non-zero if userAuthenticationRequired is true. - * It is not related to any Android user ID or UID, but is created in the - * Gatekeeper application in the secure environment. - */ - uint64_t secureUserId; - - /** - * The mac is used to authenticate the access control profile. It contains: - * - * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile) - * - * where AccessControlProfile is the CBOR map: - * - * AccessControlProfile = { - * "id": uint, - * ? "readerCertificate" : bstr, - * ? ( - * "userAuthenticationRequired" : bool, - * "timeoutMillis" : uint, - * "secureUserId" : uint - * ) - * } - */ - vec<uint8_t> mac; -}; diff --git a/identity/1.0/vts/OWNERS b/identity/1.0/vts/OWNERS deleted file mode 100644 index 6969910ce5..0000000000 --- a/identity/1.0/vts/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -swillden@google.com -zeuthen@google.com diff --git a/identity/1.0/vts/functional/Android.bp b/identity/1.0/vts/functional/Android.bp deleted file mode 100644 index 03b49de716..0000000000 --- a/identity/1.0/vts/functional/Android.bp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (C) 2019 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. -// - -cc_test { - name: "VtsHalIdentityCredentialTargetTest", - defaults: ["VtsHalTargetTestDefaults"], - srcs: [ - "VtsHalIdentityCredentialTargetTest.cpp", - ], - static_libs: [ - "android.hardware.identity@1.0", - "android.hardware.identity-support-lib", - "android.hardware.keymaster@4.0", - "libcppbor", - ], - shared_libs: [ - "libcrypto", - ], - test_suites: [ - "general-tests", - "vts-core", - ], -} diff --git a/identity/1.0/default/OWNERS b/identity/OWNERS index 6969910ce5..6969910ce5 100644 --- a/identity/1.0/default/OWNERS +++ b/identity/OWNERS diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp new file mode 100644 index 0000000000..72b19a1b49 --- /dev/null +++ b/identity/aidl/Android.bp @@ -0,0 +1,21 @@ +aidl_interface { + name: "android.hardware.identity", + vendor_available: true, + srcs: [ + "android/hardware/identity/*.aidl", + ], + imports: [ + "android.hardware.keymaster", + ], + stability: "vintf", + backend: { + java: { + platform_apis: true, + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/identity/aidl/android/hardware/identity/Certificate.aidl b/identity/aidl/android/hardware/identity/Certificate.aidl new file mode 100644 index 0000000000..5bbc17c133 --- /dev/null +++ b/identity/aidl/android/hardware/identity/Certificate.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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 android.hardware.identity; + +@VintfStability +parcelable Certificate { + /** + * encodedCertificate contains the bytes of a DER-encoded X.509 certificate. + * + * If there is no certificate, this array is empty. + */ + byte[] encodedCertificate; +} diff --git a/identity/aidl/android/hardware/identity/CipherSuite.aidl b/identity/aidl/android/hardware/identity/CipherSuite.aidl new file mode 100644 index 0000000000..20b02a8a08 --- /dev/null +++ b/identity/aidl/android/hardware/identity/CipherSuite.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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 android.hardware.identity; + +/** + * Cipher suites that can be used for communication between holder and reader devices. + */ +@VintfStability +@Backing(type="int") +enum CipherSuite { + /** + * Specifies that the cipher suite that will be used to secure communications between the reader + * is: + * + * - ECDHE with HKDF-SHA-256 for key agreement. + * - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by + * one for every message). + * - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat + * man-in-the-middle attacks), signing keys are not ephemeral. + * + * At present this is the only supported cipher suite and it is mandatory for all + * implementations to support it. + */ + CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1, +} diff --git a/identity/aidl/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/android/hardware/identity/HardwareInformation.aidl new file mode 100644 index 0000000000..d67739d94a --- /dev/null +++ b/identity/aidl/android/hardware/identity/HardwareInformation.aidl @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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 android.hardware.identity; + +@VintfStability +parcelable HardwareInformation { + /** + * credentialStoreName is the name of the credential store implementation. + */ + @utf8InCpp String credentialStoreName; + + /** + * credentialStoreAuthorName is the name of the credential store author. + */ + @utf8InCpp String credentialStoreAuthorName; + + /** + * dataChunkSize is the size of data chunks to be used when sending and recieving data + * entries. All data chunks for a data item must be this size except for the last. + */ + int dataChunkSize; + + /** + * isDirectAccess specifies whether the provisioned credential is available through + * direct access. Credentials provisioned in credential stores with this set + * to true, should use reader authentication on all data elements. + */ + boolean isDirectAccess; + + /** + * supportedDocTypes if empty, then any document type is supported, otherwise + * only the document types returned are supported. + * + * Document types are defined in the relevant standard for the document, for example for the + * for Mobile Driving License as defined by ISO 18013-5 the document type is defined to + * be "org.iso.18013.5.1.mDL". + * + */ + @utf8InCpp String[] supportedDocTypes; +} diff --git a/identity/1.0/IIdentityCredential.hal b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl index 75f6e1843d..10ce4c2867 100644 --- a/identity/1.0/IIdentityCredential.hal +++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl @@ -14,10 +14,13 @@ * limitations under the License. */ -package android.hardware.identity@1.0; +package android.hardware.identity; -import android.hardware.keymaster@4.0::HardwareAuthToken; +import android.hardware.identity.Certificate; +import android.hardware.identity.SecureAccessControlProfile; +import android.hardware.keymaster.HardwareAuthToken; +@VintfStability interface IIdentityCredential { /** * Delete a credential. @@ -35,10 +38,9 @@ interface IIdentityCredential { * After this method has been called, the persistent storage used for credentialData should * be deleted. * - * @return proofOfDeletionSignature is a COSE_Sign1 signature described above. + * @return a COSE_Sign1 signature described above. */ - deleteCredential() - generates(Result result, vec<uint8_t> proofOfDeletionSignature); + byte[] deleteCredential(); /** * Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader. @@ -46,39 +48,33 @@ interface IIdentityCredential { * with the reader. The reason for generating the key pair in the secure environment is so that * the secure environment knows what public key to expect to find in the session transcript. * - * This method may only be called once per instance. If called more than once, FAILED + * This method may only be called once per instance. If called more than once, STATUS_FAILED * will be returned. * - * @return result is OK on success or FAILED if an error occurred. - * - * @return keyPair contains the unencrypted key-pair in PKCS#8 format. + * @return the unencrypted key-pair in PKCS#8 format. */ - createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair); + byte[] createEphemeralKeyPair(); /** * Sets the public part of the reader's ephemeral key pair. * - * This method may only be called once per instance. If called more than once, FAILED + * This method may only be called once per instance. If called more than once, STATUS_FAILED * will be returned. * * @param publicKey contains the reader's ephemeral public key, in uncompressed form. - * - * @return result is OK on success or FAILED if an error occurred. */ - setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result); + void setReaderEphemeralPublicKey(in byte[] publicKey); /** * Creates a challenge value to be used for proving successful user authentication. This * is included in the authToken passed to the startRetrieval() method. * - * This method may only be called once per instance. If called more than once, FAILED + * This method may only be called once per instance. If called more than once, STATUS_FAILED * will be returned. * - * @return result is OK on success or FAILED if an error occurred. - * - * @return challenge on success, is a non-zero number. + * @return challenge, a non-zero number. */ - createAuthChallenge() generates (Result result, uint64_t challenge); + long createAuthChallenge(); /** * Start an entry retrieval process. @@ -92,12 +88,12 @@ interface IIdentityCredential { * startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), * then finally finishRetrieval()) but if this is done, the sessionTranscript parameter * must be identical for each startRetrieval() invocation. If this is not the case, this call - * fails with the SESSION_TRANSCRIPT_MISMATCH error. + * fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error. * - * If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN. + * If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN. * * Each of the provided accessControlProfiles is checked in this call. If they are not - * all valid, the call fails with INVALID_DATA. + * all valid, the call fails with STATUS_INVALID_DATA. * * For the itemsRequest parameter, the content can be defined in the way appropriate for * the credential, but there are three requirements that must be met to work with this HAL: @@ -108,7 +104,7 @@ interface IIdentityCredential { * the example below. * * If these requirements are not met the startRetrieval() call fails with - * INVALID_ITEMS_REQUEST_MESSAGE. + * STATUS_INVALID_ITEMS_REQUEST_MESSAGE. * * Here's an example of ItemsRequest CBOR which conforms to this requirement: * @@ -156,17 +152,18 @@ interface IIdentityCredential { * 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described * in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element * and there may be more (and if so, each certificate must be signed by its successor). - * This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED. + * This is checked and if the check fails the call fails with + * STATUS_READER_SIGNATURE_CHECK_FAILED. * * The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It * is permissible for this to be empty in which case the readerSignature parameter - * must also be empty. If this is not the case, the call fails with FAILED. + * must also be empty. If this is not the case, the call fails with STATUS_FAILED. * * If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public * part of the key-pair previously generated by createEphemeralKeyPair() must appear * somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in * uncompressed form. If this is not satisfied, the call fails with - * EPHEMERAL_PUBLIC_KEY_NOT_FOUND. + * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND. * * @param accessControlProfiles * Access control profiles that are required to retrieve the entries that are going to be @@ -196,16 +193,11 @@ interface IIdentityCredential { * will succeed (i.e. that the access control profile checks will succeed). This means that * it's the responsibility of the caller to determine which access control checks will fail * and remove the corresponding requests from the counts. - * - * @return result is OK on success. If an error occurs one of the values described above - * will be returned. */ - startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles, - HardwareAuthToken authToken, - vec<uint8_t> itemsRequest, - vec<uint8_t> sessionTranscript, - vec<uint8_t> readerSignature, - vec<uint16_t> requestCounts) generates(Result result); + void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles, + in HardwareAuthToken authToken, + in byte[] itemsRequest, + in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts); /** * Starts retrieving an entry, subject to access control requirements. Entries must be @@ -213,15 +205,15 @@ interface IIdentityCredential { * * If the requestData parameter as passed to startRetrieval() was non-empty * this method must only be called with entries specified in that field. If this - * requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE. + * requirement is not met, the call fails with STATUS_NOT_IN_REQUEST_MESSAGE. * - * If nameSpace or name is empty this call fails with INVALID_DATA. + * If nameSpace or name is empty this call fails with STATUS_INVALID_DATA. * * Each access control profile for the entry is checked. If user authentication * is required and the supplied auth token doesn't provide it the call fails - * with USER_AUTHENTICATION_FAILED. If reader authentication is required and + * with STATUS_USER_AUTHENTICATION_FAILED. If reader authentication is required and * a suitable reader certificate chain isn't presented, the call fails with - * READER_AUTHENTICATION_FAILED. + * STATUS_READER_AUTHENTICATION_FAILED. * * It is permissible to keep retrieving values if an access control check fails. * @@ -231,38 +223,29 @@ interface IIdentityCredential { * * @param entrySize is the size of the entry value, if it's a text string or a byte string. * It must be zero if the entry value is an integer or boolean. If this requirement - * is not met the call fails with INVALID_DATA. + * is not met the call fails with STATUS_INVALID_DATA. * * @param accessControlProfileIds specifies the set of access control profiles that can * authorize access to the provisioned element. If an identifier of a profile * is given and this profile wasn't passed to startRetrieval() this call fails - * with INVALID_DATA. - * - * @return result is OK on success. Otherwise one of INVALID_DATA, FAILED, - * USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED. + * with STATUS_INVALID_DATA. */ - startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize, - vec<uint16_t> accessControlProfileIds) - generates (Result result); - + void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, + in int entrySize, in int[] accessControlProfileIds); /** * Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize. * May only be called after startRetrieveEntry(). * * If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't - * be decoded, this call fails with INVALID_DATA. + * be decoded, this call fails with STATUS_INVALID_DATA. * * @param encryptedContent contains the encrypted and MACed content. * - * @return result is OK on success, INVALID_DATA, or FAILED if an error occurred. - * - * @return content is the entry value as CBOR, or part of the entry value in the case the + * @return the entry value as CBOR, or part of the entry value in the case the * content exceeds gcmChunkSize in length. */ - retrieveEntryValue(vec<uint8_t> encryptedContent) - generates (Result result, vec<uint8_t> content); - + byte[] retrieveEntryValue(in byte[] encryptedContent); /** * End retrieval of data, optionally returning a message authentication code over the @@ -273,11 +256,9 @@ interface IIdentityCredential { * * @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(), * below) containing the signing key to use to sign the data retrieved. If this - * is not in the right format the call fails with INVALID_DATA. - * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. + * is not in the right format the call fails with STATUS_INVALID_DATA. * - * @return mac is empty if signingKeyBlob or the sessionTranscript passed to + * @param out mac is empty if signingKeyBlob or the sessionTranscript passed to * startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload * and the detached content is set to DeviceAuthentication as defined below. * The key used for the MAC operation is EMacKey and is derived as follows: @@ -321,23 +302,17 @@ interface IIdentityCredential { * DataItemValue = any * * - * @return deviceNameSpaces the bytes of DeviceNameSpaces. + * @param out deviceNameSpaces the bytes of DeviceNameSpaces. */ - finishRetrieval(vec<uint8_t> signingKeyBlob) - generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces); - + void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces); /** * Generate a key pair to be used for signing session data and retrieved data items. * - * @return result is OK on success or FAILED if an error occurred. - * - * @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key. + * @param out signingKeyBlob contains an encrypted copy of the newly-generated private + * signing key. * - * @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed - * by the credential key. + * @return an X.509 certificate for the new signing key, signed by the credential key. */ - generateSigningKeyPair() - generates(Result result, vec<uint8_t> signingKeyBlob, - vec<uint8_t> signingKeyCertificate); -}; + Certificate generateSigningKeyPair(out byte[] signingKeyBlob); +} diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl index 118ca6fa9a..23cb1b75a2 100644 --- a/identity/1.0/IIdentityCredentialStore.hal +++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl @@ -14,10 +14,12 @@ * limitations under the License. */ -package android.hardware.identity@1.0; +package android.hardware.identity; -import IWritableIdentityCredential; -import IIdentityCredential; +import android.hardware.identity.IIdentityCredential; +import android.hardware.identity.IWritableIdentityCredential; +import android.hardware.identity.HardwareInformation; +import android.hardware.identity.CipherSuite; /** * IIdentityCredentialStore provides an interface to a secure store for user identity documents. @@ -98,37 +100,90 @@ import IIdentityCredential; * define appropriate encodings, those are used. For example, X.509 certificates. Where new * encodings are needed, CBOR is used. CBOR maps are described in CDDL notation * (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06). + * + * All binder calls in the HAL may return a ServiceSpecificException with statuses from the + * STATUS_* integers defined in this interface. Each method states which status can be returned + * and under which circumstances. */ +@VintfStability interface IIdentityCredentialStore { + /** + * Success. + */ + const int STATUS_OK = 0; + + /** + * The operation failed. This is used as a generic catch-all for errors that don't belong + * in other categories, including memory/resource allocation failures and I/O errors. + */ + const int STATUS_FAILED = 1; + + /** + * Unsupported cipher suite. + */ + const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2; + + /** + * The passed data was invalid. This is a generic catch all for errors that don't belong + * in other categories related to parameter validation. + */ + const int STATUS_INVALID_DATA = 3; + + /** + * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid. + */ + const int STATUS_INVALID_AUTH_TOKEN = 4; + + /** + * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet + * the requirements described in the documentation for that method. + */ + const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5; + + /** + * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid, + * doesn't contain an embedded certificate chain, or the signature failed to + * validate. + */ + const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6; + + /** + * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public + * key returned by createEphemeralPublicKey(). + */ + const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7; + + /** + * An access condition related to user authentication was not satisfied. + */ + const int STATUS_USER_AUTHENTICATION_FAILED = 8; + + /** + * An access condition related to reader authentication was not satisfied. + */ + const int STATUS_READER_AUTHENTICATION_FAILED = 9; + + /** + * The request data element has no access control profiles associated so it cannot be accessed. + */ + const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10; + + /** + * The requested data element is not in the provided non-empty itemsRequest message. + */ + const int STATUS_NOT_IN_REQUEST_MESSAGE = 11; + + /** + * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript. + */ + const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12; /** * Returns information about hardware. * - * The isDirectAccess output parameter indicates whether this credential store - * implementation is for direct access. Credentials provisioned in credential - * stores with this set to true, should use reader authentication on all data elements. - * - * @return result is OK on success, FAILED if an error occurred. - * - * @return credentialStoreName the name of the credential store implementation. - * - * @return credentialStoreAuthorName the name of the credential store author. - * - * @return dataChunkSize the maximum size of data chunks. - * - * @return isDirectAccess whether the provisioned credential is available through - * direct access. - * - * @return supportedDocTypes if empty, then any document type is supported, otherwise - * only the document types returned are supported. + * @return a HardwareInformation with information about the hardware. */ - getHardwareInformation() - generates(Result result, - string credentialStoreName, - string credentialStoreAuthorName, - uint32_t dataChunkSize, - bool isDirectAccess, - vec<string> supportedDocTypes); + HardwareInformation getHardwareInformation(); /** * createCredential creates a new Credential. When a Credential is created, two cryptographic @@ -147,16 +202,14 @@ interface IIdentityCredentialStore { * all-zeros hardware-bound key (HBK) and must set the test bit in the * personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential). * - * @return result is OK on success, FAILED if an error occurred. - * - * @return writableCredential is an IWritableIdentityCredential HIDL interface that provides - * operations to provision a credential. + * @return an IWritableIdentityCredential interface that provides operations to + * provision a credential. */ - createCredential(string docType, bool testCredential) - generates(Result result, IWritableIdentityCredential writableCredential); + IWritableIdentityCredential createCredential(in @utf8InCpp String docType, + in boolean testCredential); /** - * getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored + * getCredential retrieves an IIdentityCredential interface which allows use of a stored * Credential. * * The cipher suite used to communicate with the remote verifier must also be specified. Currently @@ -170,16 +223,17 @@ interface IIdentityCredentialStore { * * Support for other cipher suites may be added in a future version of this HAL. * + * This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be + * decoded or decrypted. + * + * @param cipherSuite is the cipher suite to use. + * * @param credentialData is a CBOR-encoded structure containing metadata about the credential * and an encrypted byte array that contains data used to secure the credential. See the - * return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential. - * - * @return result is OK on success or INVALID_DATA if the passed in credentialData - * cannot be decoded or decrypted. + * return argument of the same name in finishAddingEntries(), in + * IWritableIdentityCredential. * - * @return credential is an IIdentityCredential HIDL interface that provides operations on the - * Credential. + * @return an IIdentityCredential HIDL interface that provides operations on the Credential. */ - getCredential(vec<uint8_t> credentialData) - generates (Result result, IIdentityCredential credential); -}; + IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData); +} diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl index f26f76349f..483b0c7b6b 100644 --- a/identity/1.0/IWritableIdentityCredential.hal +++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl @@ -14,12 +14,16 @@ * limitations under the License. */ -package android.hardware.identity@1.0; +package android.hardware.identity; + +import android.hardware.identity.Certificate; +import android.hardware.identity.SecureAccessControlProfile; /** * IWritableIdentityCredential is used to personalize a new identity credential. Credentials cannot * be updated or modified after creation; any changes require deletion and re-creation. */ +@VintfStability interface IWritableIdentityCredential { /** * Gets the certificate chain for credentialKey which can be used to prove the hardware @@ -69,31 +73,23 @@ interface IWritableIdentityCredential { * * @param attestationChallenge a challenge set by the issuer to ensure freshness. * - * @return result is OK on success, FAILED if an error occurred. - * - * @return certificateChain is the X.509 certificate chain for the credentialKey + * @return the X.509 certificate chain for the credentialKey */ - getAttestationCertificate(vec<uint8_t> attestationApplicationId, - vec<uint8_t> attestationChallenge) - generates(Result result, vec<vec<uint8_t>> certificateChain); + Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge); /** * Start the personalization process. * * startPersonalization must not be called more than once. * - * @param accessControlProfileCount specifies the number of access control profiles that will be - * provisioned with addAccessControlProfile(). + * @param accessControlProfileCount specifies the number of access control profiles that will + * be provisioned with addAccessControlProfile(). * * @param entryCounts specifies the number of data entries that will be provisioned with * beginAddEntry() and addEntry(). Each item in the array specifies how many entries * will be added for each name space. - * - * @return result is OK on success, FAILED if an error occurred. - * */ - startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts) - generates(Result result); + void startPersonalization(in int accessControlProfileCount, in int[] entryCounts); /** * Add an access control profile, which defines the requirements or retrieval of one or more @@ -103,15 +99,15 @@ interface IWritableIdentityCredential { * * This method must be called exactly as many times as specified in the startPersonalization() * accessControlProfileCount parameter. If this is requirement is not met, the method fails - * with INVALID_DATA. + * with STATUS_INVALID_DATA. * * @param id a numeric identifier that must be unique within the context of a Credential and may * be used to reference the profile. If this is not satisfied the call fails with - * INVALID_DATA. + * STATUS_INVALID_DATA. * - * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates) - * that must be used to authenticate requests (see the readerSignature parameter in - * IIdentityCredential.startRetrieval). + * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of + * certificates) that must be used to authenticate requests (see the readerSignature + * parameter in IIdentityCredential.startRetrieval). * * @param userAuthenticationRequired if true, specifies that the user is required to * authenticate to allow requests. Required authentication freshness is specified by @@ -121,22 +117,18 @@ interface IWritableIdentityCredential { * authentication (see userAuthenticationRequired above) is valid, if * userAuthenticationRequired is true. If the timout is zero then authentication is * required for each reader session. If userAuthenticationRequired is false, the timeout - * must be zero. If this requirement is not met the call fails with INVALID_DATA. + * must be zero. If this requirement is not met the call fails with STATUS_INVALID_DATA. * * @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not * related to any Android user ID or UID, but is created in the Gatekeeper application * in the secure environment. If this requirement is not met the call fails with - * INVALID_DATA. - * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. + * STATUS_INVALID_DATA. * - * @return secureAccessControlProfile is a structure with the passed-in data and MAC created - * with storageKey for authenticating the data at a later point in time. + * @return a structure with the passed-in data and MAC created with storageKey for authenticating + * the data at a later point in time. */ - addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate, - bool userAuthenticationRequired, uint64_t timeoutMillis, - uint64_t secureUserId) - generates(Result result, SecureAccessControlProfile secureAccessControlProfile); + SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate, + in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId); /** * Begins the process of adding an entry to the credential. All access control profiles must be @@ -145,7 +137,7 @@ interface IWritableIdentityCredential { * * This method must be called exactly as many times as the sum of the items in the entryCounts * parameter specified in the startPersonalization(), and must be followed by one or more calls - * to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA. + * to addEntryValue(). If this requirement is not met the method fails with STATUS_INVALID_DATA. * * @param accessControlProfileIds specifies the set of access control profiles that can * authorize access to the provisioned element. @@ -155,13 +147,10 @@ interface IWritableIdentityCredential { * @param name is the name of the element. * * @param entrySize is the size of the entry value. If this requirement - * is not met this method fails with INVALID_DATA. - * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. + * is not met this method fails with STATUS_INVALID_DATA. */ - beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace, - string name, uint32_t entrySize) - generates(Result result); + void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, + in @utf8InCpp String name, in int entrySize); /** * Continues the process of adding an entry, providing a value or part of a value. @@ -171,18 +160,13 @@ interface IWritableIdentityCredential { * (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the * value in chunks. All chunks must be exactly gcmChunkSize except the last and the sum of all * chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this - * requirement is not met the call fails with INVALID_DATA. + * requirement is not met the call fails with STATUS_INVALID_DATA. * * @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize, * this may be partial content up to gcmChunkSize bytes long. * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. - * - * @return encryptedContent contains the encrypted and MACed content. For directly-available - * credentials the contents are implementation-defined but must not exceed 32 bytes in - * length. - * - * For other credentials, encryptedContent contains: + * @return the encrypted and MACed content. For directly-available credentials the contents are + * implementation-defined. For other credentials, the result contains * * AES-GCM-ENC(storageKey, R, Data, AdditionalData) * @@ -196,18 +180,15 @@ interface IWritableIdentityCredential { * "AccessControlProfileIds" : [ + uint ], * } */ - addEntryValue(vec<uint8_t> content) - generates(Result result, vec<uint8_t> encryptedContent); + byte[] addEntryValue(in byte[] content); /** - * Finishes adding entries and returns a signature that an issuing authority may use to validate - * that all data was provisioned correctly. + * Finishes adding entries and returns a signature that an issuing authority may use to + * validate that all data was provisioned correctly. * * After this method is called, the IWritableIdentityCredential is no longer usable. * - * @return result is OK on success or FAILED if an error occurred. - * - * @return credentialData is a CBOR-encoded structure (in CDDL notation): + * @param out credentialData is a CBOR-encoded structure (in CDDL notation): * * CredentialData = [ * tstr, ; docType, an optional name that identifies the type of credential @@ -230,10 +211,10 @@ interface IWritableIdentityCredential { * bstr ; credentialPrivKey, the private key for credentialKey * ] * - * @return proofOfProvisioningSignature proves to the IA that the credential was imported into the - * secure hardware without alteration or error. When the final addEntry() call is made - * (when the number of provisioned entries equals the sum of the items in - * startPersonalization() entryCounts parameter), it a COSE_Sign1 structure + * @param out proofOfProvisioningSignature proves to the IA that the credential was imported + * into the secure hardware without alteration or error. When the final addEntry() call is + * made (when the number of provisioned entries equals the sum of the items in + * startPersonalization() entryCounts parameter), a COSE_Sign1 structure * signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below: * * ProofOfProvisioning = [ @@ -266,7 +247,6 @@ interface IWritableIdentityCredential { * "accessControlProfiles" : [ * uint ], * } */ - finishAddingEntries() - generates(Result result, vec<uint8_t> credentialData, - vec<uint8_t> proofOfProvisioningSignature); -}; + void finishAddingEntries(out byte[] credentialData, + out byte[] proofOfProvisioningSignature); +} diff --git a/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl new file mode 100644 index 0000000000..01d312d3e6 --- /dev/null +++ b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl @@ -0,0 +1,78 @@ +/* + * Copyright 2020 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 android.hardware.identity; + +import android.hardware.identity.Certificate; + +@VintfStability +parcelable SecureAccessControlProfile { + /** + * id is a numeric identifier that must be unique within the context of a Credential and may be + * used to reference the profile. + */ + int id; + + /** + * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain + * of certificates) that must be used to authenticate requests. For details about how + * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval. + */ + Certificate readerCertificate; + + /** + * if true, the user is required to authenticate to allow requests. Required authentication + * fressness is specified by timeout below. + * + */ + boolean userAuthenticationRequired; + + /** + * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see + * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired + * is true and timout is zero then authentication is required for each reader session. + * + * If userAuthenticationRequired is false, timeout must be zero. + */ + long timeoutMillis; + + /** + * secureUserId must be non-zero if userAuthenticationRequired is true. + * It is not related to any Android user ID or UID, but is created in the + * Gatekeeper application in the secure environment. + */ + long secureUserId; + + /** + * The mac is used to authenticate the access control profile. It contains: + * + * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile) + * + * where AccessControlProfile is the CBOR map: + * + * AccessControlProfile = { + * "id": uint, + * ? "readerCertificate" : bstr, + * ? ( + * "userAuthenticationRequired" : bool, + * "timeoutMillis" : uint, + * "secureUserId" : uint + * ) + * } + */ + byte[] mac; +} + diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp new file mode 100644 index 0000000000..2eb0faa034 --- /dev/null +++ b/identity/aidl/default/Android.bp @@ -0,0 +1,29 @@ +cc_binary { + name: "android.hardware.identity-service.example", + relative_install_path: "hw", + init_rc: ["identity-default.rc"], + vintf_fragments: ["identity-default.xml"], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libcppbor", + "libcrypto", + "liblog", + "libutils", + "android.hardware.identity-support-lib", + "android.hardware.identity-ndk_platform", + "android.hardware.keymaster-ndk_platform", + ], + srcs: [ + "IdentityCredential.cpp", + "IdentityCredentialStore.cpp", + "WritableIdentityCredential.cpp", + "Util.cpp", + "service.cpp", + ], +} diff --git a/identity/1.0/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp index b0a5e568f2..d5b3a0ffee 100644 --- a/identity/1.0/default/IdentityCredential.cpp +++ b/identity/aidl/default/IdentityCredential.cpp @@ -18,6 +18,7 @@ #include "IdentityCredential.h" #include "IdentityCredentialStore.h" +#include "Util.h" #include <android/hardware/identity/support/IdentityCredentialSupport.h> @@ -28,71 +29,24 @@ #include <cppbor.h> #include <cppbor_parse.h> -namespace android { -namespace hardware { -namespace identity { -namespace implementation { +namespace aidl::android::hardware::identity { -using ::android::hardware::keymaster::V4_0::Timestamp; +using ::aidl::android::hardware::keymaster::Timestamp; using ::std::optional; -Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) { - cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_}; - vector<uint8_t> proofOfDeletion = array.encode(); - - optional<vector<uint8_t>> proofOfDeletionSignature = - support::coseSignEcDsa(credentialPrivKey_, - proofOfDeletion, // payload - {}, // additionalData - {}); // certificateChain - if (!proofOfDeletionSignature) { - _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}); - return Void(); - } - - _hidl_cb(support::resultOK(), proofOfDeletionSignature.value()); - return Void(); -} - -Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) { - optional<vector<uint8_t>> keyPair = support::createEcKeyPair(); - if (!keyPair) { - _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {}); - return Void(); - } - - // Stash public key of this key-pair for later check in startRetrieval(). - optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value()); - if (!publicKey) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error getting public part of ephemeral key pair"), - {}); - return Void(); - } - ephemeralPublicKey_ = publicKey.value(); - - _hidl_cb(support::resultOK(), keyPair.value()); - return Void(); -} +using namespace ::android::hardware::identity; -Return<void> IdentityCredential::setReaderEphemeralPublicKey( - const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) { - readerPublicKey_ = publicKey; - _hidl_cb(support::resultOK()); - return Void(); -} - -ResultCode IdentityCredential::initialize() { +int IdentityCredential::initialize() { auto [item, _, message] = cppbor::parse(credentialData_); if (item == nullptr) { LOG(ERROR) << "CredentialData is not valid CBOR: " << message; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } const cppbor::Array* arrayItem = item->asArray(); if (arrayItem == nullptr || arrayItem->size() != 3) { LOG(ERROR) << "CredentialData is not an array with three elements"; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr(); @@ -103,7 +57,7 @@ ResultCode IdentityCredential::initialize() { if (docTypeItem == nullptr || testCredentialItem == nullptr || encryptedCredentialKeysItem == nullptr) { LOG(ERROR) << "CredentialData unexpected item types"; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } docType_ = docTypeItem->value(); @@ -113,7 +67,7 @@ ResultCode IdentityCredential::initialize() { if (testCredential_) { hardwareBoundKey = support::getTestHardwareBoundKey(); } else { - hardwareBoundKey = support::getHardwareBoundKey(); + hardwareBoundKey = getHardwareBoundKey(); } const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value(); @@ -122,39 +76,83 @@ ResultCode IdentityCredential::initialize() { support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec); if (!decryptedCredentialKeys) { LOG(ERROR) << "Error decrypting CredentialKeys"; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value()); if (dckItem == nullptr) { LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } const cppbor::Array* dckArrayItem = dckItem->asArray(); if (dckArrayItem == nullptr || dckArrayItem->size() != 2) { LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements"; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr(); const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr(); if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) { LOG(ERROR) << "CredentialKeys unexpected item types"; - return ResultCode::INVALID_DATA; + return IIdentityCredentialStore::STATUS_INVALID_DATA; } storageKey_ = storageKeyItem->value(); credentialPrivKey_ = credentialPrivKeyItem->value(); - return ResultCode::OK; + return IIdentityCredentialStore::STATUS_OK; } -Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::deleteCredential( + vector<int8_t>* outProofOfDeletionSignature) { + cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_}; + vector<uint8_t> proofOfDeletion = array.encode(); + + optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_, + proofOfDeletion, // payload + {}, // additionalData + {}); // certificateChain + if (!signature) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error signing data")); + } + + *outProofOfDeletionSignature = byteStringToSigned(signature.value()); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) { + optional<vector<uint8_t>> kp = support::createEcKeyPair(); + if (!kp) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair")); + } + + // Stash public key of this key-pair for later check in startRetrieval(). + optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value()); + if (!publicKey) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting public part of ephemeral key pair")); + } + ephemeralPublicKey_ = publicKey.value(); + + *outKeyPair = byteStringToSigned(kp.value()); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey( + const vector<int8_t>& publicKey) { + readerPublicKey_ = byteStringToUnsigned(publicKey); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) { uint64_t challenge = 0; while (challenge == 0) { optional<vector<uint8_t>> bytes = support::getRandom(8); if (!bytes) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"), - 0); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting random data for challenge")); } challenge = 0; @@ -163,17 +161,16 @@ Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hid } } - authChallenge_ = challenge; - _hidl_cb(support::resultOK(), challenge); - return Void(); + *outChallenge = challenge; + return ndk::ScopedAStatus::ok(); } // TODO: this could be a lot faster if we did all the splitting and pubkey extraction // ahead of time. bool checkReaderAuthentication(const SecureAccessControlProfile& profile, const vector<uint8_t>& readerCertificateChain) { - optional<vector<uint8_t>> acpPubKey = - support::certificateChainGetTopMostKey(profile.readerCertificate); + optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey( + byteStringToUnsigned(profile.readerCertificate.encodedCertificate)); if (!acpPubKey) { LOG(ERROR) << "Error extracting public key from readerCertificate in profile"; return false; @@ -202,7 +199,9 @@ bool checkReaderAuthentication(const SecureAccessControlProfile& profile, Timestamp clockGetTime() { struct timespec time; clock_gettime(CLOCK_MONOTONIC, &time); - return time.tv_sec * 1000 + time.tv_nsec / 1000000; + Timestamp ts; + ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000; + return ts; } bool checkUserAuthentication(const SecureAccessControlProfile& profile, @@ -219,7 +218,7 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, return false; } - if (authToken.challenge != authChallenge) { + if (authToken.challenge != int64_t(authChallenge)) { LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created"; return false; } @@ -239,42 +238,44 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, // implementation should never be used on a real device. // Timestamp now = clockGetTime(); - if (authToken.timestamp > now) { - LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp - << ") is in the future (now: " << now << ")"; + if (authToken.timestamp.milliSeconds > now.milliSeconds) { + LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds + << ") is in the future (now: " << now.milliSeconds << ")"; return false; } - if (now > authToken.timestamp + profile.timeoutMillis) { - LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + " + if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) { + LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + " << profile.timeoutMillis << " = " - << (authToken.timestamp + profile.timeoutMillis) - << ") is in the past (now: " << now << ")"; + << (authToken.timestamp.milliSeconds + profile.timeoutMillis) + << ") is in the past (now: " << now.milliSeconds << ")"; return false; } - return true; } -Return<void> IdentityCredential::startRetrieval( - const hidl_vec<SecureAccessControlProfile>& accessControlProfiles, - const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest, - const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature, - const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::startRetrieval( + const vector<SecureAccessControlProfile>& accessControlProfiles, + const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS, + const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS, + const vector<int32_t>& requestCounts) { + auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS); + auto itemsRequest = byteStringToUnsigned(itemsRequestS); + auto readerSignature = byteStringToUnsigned(readerSignatureS); + if (sessionTranscript.size() > 0) { auto [item, _, message] = cppbor::parse(sessionTranscript); if (item == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "SessionTranscript contains invalid CBOR")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "SessionTranscript contains invalid CBOR")); } sessionTranscriptItem_ = std::move(item); } if (numStartRetrievalCalls_ > 0) { - if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) { - _hidl_cb(support::result( - ResultCode::SESSION_TRANSCRIPT_MISMATCH, + if (sessionTranscript_ != sessionTranscript) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH, "Passed-in SessionTranscript doesn't match previously used SessionTranscript")); - return Void(); } } sessionTranscript_ = sessionTranscript; @@ -285,23 +286,23 @@ Return<void> IdentityCredential::startRetrieval( if (readerSignature.size() > 0) { readerCertificateChain = support::coseSignGetX5Chain(readerSignature); if (!readerCertificateChain) { - _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED, - "Unable to get reader certificate chain from COSE_Sign1")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED, + "Unable to get reader certificate chain from COSE_Sign1")); } if (!support::certificateChainValidate(readerCertificateChain.value())) { - _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED, - "Error validating reader certificate chain")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED, + "Error validating reader certificate chain")); } optional<vector<uint8_t>> readerPublicKey = support::certificateChainGetTopMostKey(readerCertificateChain.value()); if (!readerPublicKey) { - _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED, - "Unable to get public key from reader certificate chain")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED, + "Unable to get public key from reader certificate chain")); } const vector<uint8_t>& itemsRequestBytes = itemsRequest; @@ -313,9 +314,9 @@ Return<void> IdentityCredential::startRetrieval( if (!support::coseCheckEcDsaSignature(readerSignature, dataThatWasSigned, // detached content readerPublicKey.value())) { - _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED, - "readerSignature check failed")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED, + "readerSignature check failed")); } } @@ -334,39 +335,39 @@ Return<void> IdentityCredential::startRetrieval( if (sessionTranscript.size() > 0) { const cppbor::Array* array = sessionTranscriptItem_->asArray(); if (array == nullptr || array->size() != 2) { - _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "SessionTranscript is not an array with two items")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "SessionTranscript is not an array with two items")); } const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic(); if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) { - _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "First item in SessionTranscript array is not a " - "semantic with value 24")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "First item in SessionTranscript array is not a " + "semantic with value 24")); } const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr(); if (encodedDE == nullptr) { - _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "Child of semantic in first item in SessionTranscript " - "array is not a bstr")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "Child of semantic in first item in SessionTranscript " + "array is not a bstr")); } const vector<uint8_t>& bytesDE = encodedDE->value(); auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_); if (!getXYSuccess) { - _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "Error extracting X and Y from ePub")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "Error extracting X and Y from ePub")); } if (sessionTranscript.size() > 0 && !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr && memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) { - _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "Did not find ephemeral public key's X and Y coordinates in " - "SessionTranscript (make sure leading zeroes are not used)")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "Did not find ephemeral public key's X and Y coordinates in " + "SessionTranscript (make sure leading zeroes are not used)")); } } @@ -378,17 +379,17 @@ Return<void> IdentityCredential::startRetrieval( // 1. The content must be a CBOR-encoded structure. auto [item, _, message] = cppbor::parse(itemsRequest); if (item == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE, - "Error decoding CBOR in itemsRequest: %s", message.c_str())); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, + "Error decoding CBOR in itemsRequest")); } // 2. The CBOR structure must be a map. const cppbor::Map* map = item->asMap(); if (map == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE, - "itemsRequest is not a CBOR map")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, + "itemsRequest is not a CBOR map")); } // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in @@ -435,9 +436,9 @@ Return<void> IdentityCredential::startRetrieval( } } if (nsMap == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE, - "No nameSpaces map in top-most map")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, + "No nameSpaces map in top-most map")); } for (size_t n = 0; n < nsMap->size(); n++) { @@ -445,9 +446,9 @@ Return<void> IdentityCredential::startRetrieval( const cppbor::Tstr* nsKey = nsKeyItem->asTstr(); const cppbor::Map* nsInnerMap = nsValueItem->asMap(); if (nsKey == nullptr || nsInnerMap == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE, - "Type mismatch in nameSpaces map")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, + "Type mismatch in nameSpaces map")); } string requestedNamespace = nsKey->value(); vector<string> requestedKeys; @@ -458,9 +459,9 @@ Return<void> IdentityCredential::startRetrieval( const cppbor::Bool* intentToRetainItem = (simple != nullptr) ? simple->asBool() : nullptr; if (nameItem == nullptr || intentToRetainItem == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE, - "Type mismatch in value in nameSpaces map")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, + "Type mismatch in value in nameSpaces map")); } requestedKeys.push_back(nameItem->value()); } @@ -471,20 +472,20 @@ Return<void> IdentityCredential::startRetrieval( // Finally, validate all the access control profiles in the requestData. bool haveAuthToken = (authToken.mac.size() > 0); for (const auto& profile : accessControlProfiles) { - if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Error checking MAC for profile with id %d", int(profile.id))); - return Void(); + if (!secureAccessControlProfileCheckMac(profile, storageKey_)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Error checking MAC for profile")); } - ResultCode accessControlCheck = ResultCode::OK; + int accessControlCheck = IIdentityCredentialStore::STATUS_OK; if (profile.userAuthenticationRequired) { if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) { - accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED; + accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED; } - } else if (profile.readerCertificate.size() > 0) { + } else if (profile.readerCertificate.encodedCertificate.size() > 0) { if (!readerCertificateChain || !checkReaderAuthentication(profile, readerCertificateChain.value())) { - accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED; + accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED; } } profileIdToAccessCheckResult_[profile.id] = accessControlCheck; @@ -499,26 +500,25 @@ Return<void> IdentityCredential::startRetrieval( itemsRequest_ = itemsRequest; numStartRetrievalCalls_ += 1; - _hidl_cb(support::resultOK()); - return Void(); + return ndk::ScopedAStatus::ok(); } -Return<void> IdentityCredential::startRetrieveEntryValue( - const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize, - const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( + const string& nameSpace, const string& name, int32_t entrySize, + const vector<int32_t>& accessControlProfileIds) { if (name.empty()) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty")); } if (nameSpace.empty()) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty")); } if (requestCountsRemaining_.size() == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "No more name spaces left to go through")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "No more name spaces left to go through")); } if (currentNameSpace_ == "") { @@ -529,19 +529,18 @@ Return<void> IdentityCredential::startRetrieveEntryValue( if (nameSpace == currentNameSpace_) { // Same namespace. if (requestCountsRemaining_[0] == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "No more entries to be retrieved in current name space")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "No more entries to be retrieved in current name space")); } requestCountsRemaining_[0] -= 1; } else { // New namespace. if (requestCountsRemaining_[0] != 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Moved to new name space but %d entries need to be retrieved " - "in current name space", - int(requestCountsRemaining_[0]))); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Moved to new name space but one or more entries need to be retrieved " + "in current name space")); } if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) { deviceNameSpacesMap_.add(currentNameSpace_, @@ -558,18 +557,15 @@ Return<void> IdentityCredential::startRetrieveEntryValue( if (itemsRequest_.size() > 0) { const auto& it = requestedNameSpacesAndNames_.find(nameSpace); if (it == requestedNameSpacesAndNames_.end()) { - _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE, - "Name space '%s' was not requested in startRetrieval", - nameSpace.c_str())); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, + "Name space was not requested in startRetrieval")); } const auto& dataItemNames = it->second; if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) { - _hidl_cb(support::result( - ResultCode::NOT_IN_REQUEST_MESSAGE, - "Data item name '%s' in name space '%s' was not requested in startRetrieval", - name.c_str(), nameSpace.c_str())); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, + "Data item name in name space was not requested in startRetrieval")); } } @@ -579,44 +575,44 @@ Return<void> IdentityCredential::startRetrieveEntryValue( // // If an item is configured without any profiles, access is denied. // - ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES; + int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES; for (auto id : accessControlProfileIds) { auto search = profileIdToAccessCheckResult_.find(id); if (search == profileIdToAccessCheckResult_.end()) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Requested entry with unvalidated profile id %d", (int(id)))); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Requested entry with unvalidated profile id")); } - ResultCode accessControlForProfile = search->second; - if (accessControlForProfile == ResultCode::OK) { - accessControl = ResultCode::OK; + int accessControlForProfile = search->second; + if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) { + accessControl = IIdentityCredentialStore::STATUS_OK; break; } accessControl = accessControlForProfile; } - if (accessControl != ResultCode::OK) { - _hidl_cb(support::result(accessControl, "Access control check failed")); - return Void(); + if (accessControl != IIdentityCredentialStore::STATUS_OK) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + int(accessControl), "Access control check failed")); } - entryAdditionalData_ = - support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); + entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); currentName_ = name; entryRemainingBytes_ = entrySize; entryValue_.resize(0); - _hidl_cb(support::resultOK()); - return Void(); + return ndk::ScopedAStatus::ok(); } -Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent, - retrieveEntryValue_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS, + vector<int8_t>* outContent) { + auto encryptedContent = byteStringToUnsigned(encryptedContentS); + optional<vector<uint8_t>> content = support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_); if (!content) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data")); } size_t chunkSize = content.value().size(); @@ -624,23 +620,17 @@ Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& enc if (chunkSize > entryRemainingBytes_) { LOG(ERROR) << "Retrieved chunk of size " << chunkSize << " is bigger than remaining space of size " << entryRemainingBytes_; - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Retrieved chunk of size %zd is bigger than remaining space " - "of size %zd", - chunkSize, entryRemainingBytes_), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Retrieved chunk is bigger than remaining space")); } entryRemainingBytes_ -= chunkSize; if (entryRemainingBytes_ > 0) { if (chunkSize != IdentityCredentialStore::kGcmChunkSize) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Retrieved non-final chunk of size %zd but expected " - "kGcmChunkSize which is %zd", - chunkSize, IdentityCredentialStore::kGcmChunkSize), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Retrieved non-final chunk of size which isn't kGcmChunkSize")); } } @@ -649,18 +639,22 @@ Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& enc if (entryRemainingBytes_ == 0) { auto [entryValueItem, _, message] = cppbor::parse(entryValue_); if (entryValueItem == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Retrieved data which is invalid CBOR")); } currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem)); } - _hidl_cb(support::resultOK(), content.value()); - return Void(); + *outContent = byteStringToSigned(content.value()); + return ndk::ScopedAStatus::ok(); } -Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob, - finishRetrieval_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS, + vector<int8_t>* outMac, + vector<int8_t>* outDeviceNameSpaces) { + auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS); + if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) { deviceNameSpacesMap_.add(currentNameSpace_, std::move(currentNameSpaceDeviceNameSpacesMap_)); @@ -682,40 +676,42 @@ Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signin optional<vector<uint8_t>> signingKey = support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob); if (!signingKey) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"), - {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Error decrypting signingKeyBlob")); } optional<vector<uint8_t>> sharedSecret = support::ecdh(readerPublicKey_, signingKey.value()); if (!sharedSecret) { - _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH")); } vector<uint8_t> salt = {0x00}; vector<uint8_t> info = {}; optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32); if (!derivedKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"), - {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error deriving key from shared secret")); } mac = support::coseMac0(derivedKey.value(), {}, // payload encodedDeviceAuthentication); // additionalData if (!mac) { - _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error MACing data")); } } - _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces); - return Void(); + *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({}))); + *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces); + return ndk::ScopedAStatus::ok(); } -Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair( + vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) { string serialDecimal = "0"; // TODO: set serial to something unique string issuer = "Android Open Source Project"; string subject = "Android IdentityCredential Reference Implementation"; @@ -724,50 +720,49 @@ Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_c optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair(); if (!signingKeyPKCS8) { - _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey")); } optional<vector<uint8_t>> signingPublicKey = support::ecKeyPairGetPublicKey(signingKeyPKCS8.value()); if (!signingPublicKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {}, - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting public part of signingKey")); } optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value()); if (!signingKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"), - {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting private part of signingKey")); } optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate( signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject, validityNotBefore, validityNotAfter); if (!certificate) { - _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey")); } optional<vector<uint8_t>> nonce = support::getRandom(12); if (!nonce) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error getting random")); } vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end()); optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm( storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob); if (!encryptedSigningKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey")); } - _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value()); - return Void(); + *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value()); + *outSigningKeyCertificate = Certificate(); + outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value()); + return ndk::ScopedAStatus::ok(); } -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android +} // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h new file mode 100644 index 0000000000..49ed0d45b8 --- /dev/null +++ b/identity/aidl/default/IdentityCredential.h @@ -0,0 +1,109 @@ +/* + * Copyright 2019, 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. + */ + +#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H +#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H + +#include <aidl/android/hardware/identity/BnIdentityCredential.h> +#include <aidl/android/hardware/keymaster/HardwareAuthToken.h> +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include <cppbor/cppbor.h> + +namespace aidl::android::hardware::identity { + +using ::aidl::android::hardware::keymaster::HardwareAuthToken; +using ::std::map; +using ::std::string; +using ::std::vector; + +using MapStringToVectorOfStrings = map<string, vector<string>>; + +class IdentityCredential : public BnIdentityCredential { + public: + IdentityCredential(const vector<uint8_t>& credentialData) + : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {} + + // Parses and decrypts credentialData_, return a status code from + // IIdentityCredentialStore. Must be called right after construction. + int initialize(); + + // Methods from IIdentityCredential follow. + ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override; + ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override; + ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override; + ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override; + ndk::ScopedAStatus startRetrieval( + const vector<SecureAccessControlProfile>& accessControlProfiles, + const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest, + const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature, + const vector<int32_t>& requestCounts) override; + ndk::ScopedAStatus startRetrieveEntryValue( + const string& nameSpace, const string& name, int32_t entrySize, + const vector<int32_t>& accessControlProfileIds) override; + ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent, + vector<int8_t>* outContent) override; + ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac, + vector<int8_t>* outDeviceNameSpaces) override; + ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob, + Certificate* outSigningKeyCertificate) override; + + private: + // Set by constructor + vector<uint8_t> credentialData_; + int numStartRetrievalCalls_; + + // Set by initialize() + string docType_; + bool testCredential_; + vector<uint8_t> storageKey_; + vector<uint8_t> credentialPrivKey_; + + // Set by createEphemeralKeyPair() + vector<uint8_t> ephemeralPublicKey_; + + // Set by setReaderEphemeralPublicKey() + vector<uint8_t> readerPublicKey_; + + // Set by createAuthChallenge() + uint64_t authChallenge_; + + // Set at startRetrieval() time. + map<int32_t, int> profileIdToAccessCheckResult_; + vector<uint8_t> sessionTranscript_; + std::unique_ptr<cppbor::Item> sessionTranscriptItem_; + vector<uint8_t> itemsRequest_; + vector<int32_t> requestCountsRemaining_; + MapStringToVectorOfStrings requestedNameSpacesAndNames_; + cppbor::Map deviceNameSpacesMap_; + cppbor::Map currentNameSpaceDeviceNameSpacesMap_; + + // Set at startRetrieveEntryValue() time. + string currentNameSpace_; + string currentName_; + size_t entryRemainingBytes_; + vector<uint8_t> entryValue_; + vector<uint8_t> entryAdditionalData_; +}; + +} // namespace aidl::android::hardware::identity + +#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/IdentityCredentialStore.cpp new file mode 100644 index 0000000000..1efb4b4937 --- /dev/null +++ b/identity/aidl/default/IdentityCredentialStore.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2019, 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. + */ + +#define LOG_TAG "IdentityCredentialStore" + +#include <android-base/logging.h> + +#include "IdentityCredential.h" +#include "IdentityCredentialStore.h" +#include "WritableIdentityCredential.h" + +namespace aidl::android::hardware::identity { + +ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation( + HardwareInformation* hardwareInformation) { + HardwareInformation hw; + hw.credentialStoreName = "Identity Credential Reference Implementation"; + hw.credentialStoreAuthorName = "Google"; + hw.dataChunkSize = kGcmChunkSize; + hw.isDirectAccess = false; + hw.supportedDocTypes = {}; + *hardwareInformation = hw; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredentialStore::createCredential( + const string& docType, bool testCredential, + shared_ptr<IWritableIdentityCredential>* outWritableCredential) { + shared_ptr<WritableIdentityCredential> wc = + ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential); + if (!wc->initialize()) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error initializing WritableIdentityCredential")); + } + *outWritableCredential = wc; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredentialStore::getCredential( + CipherSuite cipherSuite, const vector<int8_t>& credentialData, + shared_ptr<IIdentityCredential>* outCredential) { + // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now. + if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED, + "Unsupported cipher suite")); + } + + vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end()); + shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data); + auto ret = credential->initialize(); + if (ret != IIdentityCredentialStore::STATUS_OK) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + int(ret), "Error initializing IdentityCredential")); + } + *outCredential = credential; + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/IdentityCredentialStore.h new file mode 100644 index 0000000000..a2051130b0 --- /dev/null +++ b/identity/aidl/default/IdentityCredentialStore.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019, 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. + */ + +#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H +#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H + +#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h> + +namespace aidl::android::hardware::identity { + +using ::std::shared_ptr; +using ::std::string; +using ::std::vector; + +class IdentityCredentialStore : public BnIdentityCredentialStore { + public: + IdentityCredentialStore() {} + + // The GCM chunk size used by this implementation is 64 KiB. + static constexpr size_t kGcmChunkSize = 64 * 1024; + + // Methods from IIdentityCredentialStore follow. + ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override; + + ndk::ScopedAStatus createCredential( + const string& docType, bool testCredential, + shared_ptr<IWritableIdentityCredential>* outWritableCredential) override; + + ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData, + shared_ptr<IIdentityCredential>* outCredential) override; +}; + +} // namespace aidl::android::hardware::identity + +#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp new file mode 100644 index 0000000000..a0f86bedcd --- /dev/null +++ b/identity/aidl/default/Util.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2019, 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. + */ + +#define LOG_TAG "Util" + +#include "Util.h" + +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <string.h> + +#include <android-base/logging.h> + +#include <cppbor.h> +#include <cppbor_parse.h> + +namespace aidl::android::hardware::identity { + +using namespace ::android::hardware::identity; + +// This is not a very random HBK but that's OK because this is the SW +// implementation where it can't be kept secret. +vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +const vector<uint8_t>& getHardwareBoundKey() { + return hardwareBoundKey; +} + +vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) { + return vector<uint8_t>(value.begin(), value.end()); +} + +vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) { + return vector<int8_t>(value.begin(), value.end()); +} + +vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) { + cppbor::Map map; + map.add("id", profile.id); + + if (profile.readerCertificate.encodedCertificate.size() > 0) { + map.add("readerCertificate", + cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate))); + } + + if (profile.userAuthenticationRequired) { + map.add("userAuthenticationRequired", profile.userAuthenticationRequired); + map.add("timeoutMillis", profile.timeoutMillis); + map.add("secureUserId", profile.secureUserId); + } + + return map.encode(); +} + +optional<vector<uint8_t>> secureAccessControlProfileCalcMac( + const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) { + vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile); + + optional<vector<uint8_t>> nonce = support::getRandom(12); + if (!nonce) { + return {}; + } + optional<vector<uint8_t>> macO = + support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData); + if (!macO) { + return {}; + } + return macO.value(); +} + +bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, + const vector<uint8_t>& storageKey) { + vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile); + + if (profile.mac.size() < support::kAesGcmIvSize) { + return false; + } + vector<uint8_t> nonce = + vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize); + optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData); + if (!mac) { + return false; + } + if (mac.value() != byteStringToUnsigned(profile.mac)) { + return false; + } + return true; +} + +vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name, + const vector<int32_t> accessControlProfileIds) { + cppbor::Map map; + map.add("Namespace", nameSpace); + map.add("Name", name); + + cppbor::Array acpIds; + for (auto id : accessControlProfileIds) { + acpIds.add(id); + } + map.add("AccessControlProfileIds", std::move(acpIds)); + return map.encode(); +} + +} // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h new file mode 100644 index 0000000000..ee41ad1aac --- /dev/null +++ b/identity/aidl/default/Util.h @@ -0,0 +1,58 @@ +/* + * Copyright 2019, 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. + */ + +#ifndef ANDROID_HARDWARE_IDENTITY_UTIL_H +#define ANDROID_HARDWARE_IDENTITY_UTIL_H + +#include <aidl/android/hardware/identity/BnIdentityCredential.h> +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <map> +#include <optional> +#include <set> +#include <string> +#include <vector> + +#include <cppbor/cppbor.h> + +namespace aidl::android::hardware::identity { + +using ::std::optional; +using ::std::string; +using ::std::vector; + +// Returns the hardware-bound AES-128 key. +const vector<uint8_t>& getHardwareBoundKey(); + +// Calculates the MAC for |profile| using |storageKey|. +optional<vector<uint8_t>> secureAccessControlProfileCalcMac( + const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey); + +// Checks authenticity of the MAC in |profile| using |storageKey|. +bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, + const vector<uint8_t>& storageKey); + +// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method. +vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name, + const vector<int32_t> accessControlProfileIds); + +vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value); + +vector<int8_t> byteStringToSigned(const vector<uint8_t>& value); + +} // namespace aidl::android::hardware::identity + +#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp index 4c39f85eb3..ba2062d7c0 100644 --- a/identity/1.0/default/WritableIdentityCredential.cpp +++ b/identity/aidl/default/WritableIdentityCredential.cpp @@ -16,9 +16,6 @@ #define LOG_TAG "WritableIdentityCredential" -#include "WritableIdentityCredential.h" -#include "IdentityCredentialStore.h" - #include <android/hardware/identity/support/IdentityCredentialSupport.h> #include <android-base/logging.h> @@ -26,56 +23,14 @@ #include <cppbor/cppbor.h> #include <cppbor/cppbor_parse.h> -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -using ::std::optional; - -// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and -// |credentialPrivKey|. -static bool generateCredentialKeys(const vector<uint8_t>& storageKey, - const vector<uint8_t>& credentialPrivKey, - vector<uint8_t>& credentialKeys) { - if (storageKey.size() != 16) { - LOG(ERROR) << "Size of storageKey is not 16"; - return false; - } +#include "IdentityCredentialStore.h" +#include "Util.h" +#include "WritableIdentityCredential.h" - cppbor::Array array; - array.add(cppbor::Bstr(storageKey)); - array.add(cppbor::Bstr(credentialPrivKey)); - credentialKeys = array.encode(); - return true; -} +namespace aidl::android::hardware::identity { -// Writes CBOR-encoded structure to |credentialData| containing |docType|, -// |testCredential| and |credentialKeys|. The latter element will be stored in -// encrypted form, using |hardwareBoundKey| as the encryption key. -bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType, - bool testCredential, const vector<uint8_t>& credentialKeys, - vector<uint8_t>& credentialData) { - optional<vector<uint8_t>> nonce = support::getRandom(12); - if (!nonce) { - LOG(ERROR) << "Error getting random"; - return false; - } - vector<uint8_t> docTypeAsVec(docType.begin(), docType.end()); - optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm( - hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec); - if (!credentialBlob) { - LOG(ERROR) << "Error encrypting CredentialKeys blob"; - return false; - } - - cppbor::Array array; - array.add(docType); - array.add(testCredential); - array.add(cppbor::Bstr(credentialBlob.value())); - credentialData = array.encode(); - return true; -} +using ::std::optional; +using namespace ::android::hardware::identity; bool WritableIdentityCredential::initialize() { optional<vector<uint8_t>> keyPair = support::createEcKeyPair(); @@ -112,34 +67,32 @@ bool WritableIdentityCredential::initialize() { // ensure the returned certificate chain satisfy the requirements listed in // the docs for IWritableIdentityCredential::getAttestationCertificate() // -Return<void> WritableIdentityCredential::getAttestationCertificate( - const hidl_vec<uint8_t>& /* attestationApplicationId */, - const hidl_vec<uint8_t>& /* attestationChallenge */, - getAttestationCertificate_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate( + const vector<int8_t>& /*attestationApplicationId*/, + const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) { // For now, we dynamically generate an attestion key on each and every // request and use that to sign CredentialKey. In a real implementation this // would look very differently. optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair(); if (!attestationKeyPair) { - _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey")); } optional<vector<uint8_t>> attestationPubKey = support::ecKeyPairGetPublicKey(attestationKeyPair.value()); if (!attestationPubKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting public part of attestationKey")); } optional<vector<uint8_t>> attestationPrivKey = support::ecKeyPairGetPrivateKey(attestationKeyPair.value()); if (!attestationPrivKey) { - _hidl_cb( - support::result(ResultCode::FAILED, "Error getting private part of attestationKey"), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting private part of attestationKey")); } string serialDecimal; @@ -158,10 +111,9 @@ Return<void> WritableIdentityCredential::getAttestationCertificate( credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject, validityNotBefore, validityNotAfter); if (!credentialPubKeyCertificate) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error creating certificate for credentialPubKey"), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error creating certificate for credentialPubKey")); } // This is followed by a certificate for |attestationPubKey| self-signed by @@ -173,10 +125,9 @@ Return<void> WritableIdentityCredential::getAttestationCertificate( attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject, validityNotBefore, validityNotAfter); if (!attestationKeyCertificate) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error creating certificate for attestationPubKey"), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error creating certificate for attestationPubKey")); } // Concatenate the certificates to form the chain. @@ -189,19 +140,20 @@ Return<void> WritableIdentityCredential::getAttestationCertificate( optional<vector<vector<uint8_t>>> splitCertChain = support::certificateChainSplit(certificateChain); if (!splitCertChain) { - _hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {}); - return Void(); - } - hidl_vec<hidl_vec<uint8_t>> ret; - ret.resize(splitCertChain.value().size()); - std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin()); - _hidl_cb(support::resultOK(), ret); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain")); + } + *outCertificateChain = vector<Certificate>(); + for (const vector<uint8_t>& cert : splitCertChain.value()) { + Certificate c = Certificate(); + c.encodedCertificate = byteStringToSigned(cert); + outCertificateChain->push_back(std::move(c)); + } + return ndk::ScopedAStatus::ok(); } -Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount, - const hidl_vec<uint16_t>& entryCounts, - startPersonalization_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::startPersonalization( + int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) { numAccessControlProfileRemaining_ = accessControlProfileCount; remainingEntryCounts_ = entryCounts; entryNameSpace_ = ""; @@ -210,29 +162,27 @@ Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessCon signedDataNamespaces_ = cppbor::Map(); signedDataCurrentNamespace_ = cppbor::Array(); - _hidl_cb(support::resultOK()); - return Void(); + return ndk::ScopedAStatus::ok(); } -Return<void> WritableIdentityCredential::addAccessControlProfile( - uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired, - uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile( + int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired, + int64_t timeoutMillis, int64_t secureUserId, + SecureAccessControlProfile* outSecureAccessControlProfile) { SecureAccessControlProfile profile; if (numAccessControlProfileRemaining_ == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "numAccessControlProfileRemaining_ is 0 and expected non-zero"), - profile); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "numAccessControlProfileRemaining_ is 0 and expected non-zero")); } // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also // be zero. if (!userAuthenticationRequired && timeoutMillis != 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "userAuthenticationRequired is false but timeout is non-zero"), - profile); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "userAuthenticationRequired is false but timeout is non-zero")); } profile.id = id; @@ -240,18 +190,19 @@ Return<void> WritableIdentityCredential::addAccessControlProfile( profile.userAuthenticationRequired = userAuthenticationRequired; profile.timeoutMillis = timeoutMillis; profile.secureUserId = secureUserId; - optional<vector<uint8_t>> mac = - support::secureAccessControlProfileCalcMac(profile, storageKey_); + optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_); if (!mac) { - _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile")); } - profile.mac = mac.value(); + profile.mac = byteStringToSigned(mac.value()); cppbor::Map profileMap; profileMap.add("id", profile.id); - if (profile.readerCertificate.size() > 0) { - profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate)); + if (profile.readerCertificate.encodedCertificate.size() > 0) { + profileMap.add( + "readerCertificate", + cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate))); } if (profile.userAuthenticationRequired) { profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired); @@ -261,25 +212,24 @@ Return<void> WritableIdentityCredential::addAccessControlProfile( numAccessControlProfileRemaining_--; - _hidl_cb(support::resultOK(), profile); - return Void(); + *outSecureAccessControlProfile = profile; + return ndk::ScopedAStatus::ok(); } -Return<void> WritableIdentityCredential::beginAddEntry( - const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace, - const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry( + const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name, + int32_t entrySize) { if (numAccessControlProfileRemaining_ != 0) { LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_ << " and expected zero"; - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "numAccessControlProfileRemaining_ is %zd and expected zero", - numAccessControlProfileRemaining_)); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "numAccessControlProfileRemaining_ is not zero")); } if (remainingEntryCounts_.size() == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to")); } // Handle initial beginEntry() call. @@ -291,10 +241,9 @@ Return<void> WritableIdentityCredential::beginAddEntry( if (nameSpace != entryNameSpace_) { // Then check that all entries in the previous namespace have been added.. if (remainingEntryCounts_[0] != 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "New namespace but %d entries remain to be added", - int(remainingEntryCounts_[0]))); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "New namespace but a non-zero number of entries remain to be added")); } remainingEntryCounts_.erase(remainingEntryCounts_.begin()); @@ -305,15 +254,14 @@ Return<void> WritableIdentityCredential::beginAddEntry( } else { // Same namespace... if (remainingEntryCounts_[0] == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Same namespace but no entries remain to be added")); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Same namespace but no entries remain to be added")); } remainingEntryCounts_[0] -= 1; } - entryAdditionalData_ = - support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); + entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); entryRemainingBytes_ = entrySize; entryNameSpace_ = nameSpace; @@ -321,55 +269,45 @@ Return<void> WritableIdentityCredential::beginAddEntry( entryAccessControlProfileIds_ = accessControlProfileIds; entryBytes_.resize(0); // LOG(INFO) << "name=" << name << " entrySize=" << entrySize; - - _hidl_cb(support::resultOK()); - return Void(); + return ndk::ScopedAStatus::ok(); } -Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content, - addEntryValue_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS, + vector<int8_t>* outEncryptedContent) { + auto content = byteStringToUnsigned(contentS); size_t contentSize = content.size(); if (contentSize > IdentityCredentialStore::kGcmChunkSize) { - _hidl_cb(support::result( - ResultCode::INVALID_DATA, - "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd", - contentSize, IdentityCredentialStore::kGcmChunkSize), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Passed in chunk of is bigger than kGcmChunkSize")); } if (contentSize > entryRemainingBytes_) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Passed in chunk of size %zd is bigger than remaining space " - "of size %zd", - contentSize, entryRemainingBytes_), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Passed in chunk is bigger than remaining space")); } entryBytes_.insert(entryBytes_.end(), content.begin(), content.end()); entryRemainingBytes_ -= contentSize; if (entryRemainingBytes_ > 0) { if (contentSize != IdentityCredentialStore::kGcmChunkSize) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "Retrieved non-final chunk of size %zd but expected " - "kGcmChunkSize which is %zd", - contentSize, IdentityCredentialStore::kGcmChunkSize), - {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Retrieved non-final chunk which isn't kGcmChunkSize")); } } optional<vector<uint8_t>> nonce = support::getRandom(12); if (!nonce) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce")); } optional<vector<uint8_t>> encryptedContent = support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_); if (!encryptedContent) { - _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content")); } if (entryRemainingBytes_ == 0) { @@ -377,8 +315,8 @@ Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& // CBOR). auto [item, _, message] = cppbor::parse(entryBytes_); if (item == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR")); } cppbor::Map entryMap; entryMap.add("name", entryName_); @@ -391,11 +329,56 @@ Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& signedDataCurrentNamespace_.add(std::move(entryMap)); } - _hidl_cb(support::resultOK(), encryptedContent.value()); - return Void(); + *outEncryptedContent = byteStringToSigned(encryptedContent.value()); + return ndk::ScopedAStatus::ok(); +} + +// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and +// |credentialPrivKey|. +static bool generateCredentialKeys(const vector<uint8_t>& storageKey, + const vector<uint8_t>& credentialPrivKey, + vector<uint8_t>& credentialKeys) { + if (storageKey.size() != 16) { + LOG(ERROR) << "Size of storageKey is not 16"; + return false; + } + + cppbor::Array array; + array.add(cppbor::Bstr(storageKey)); + array.add(cppbor::Bstr(credentialPrivKey)); + credentialKeys = array.encode(); + return true; +} + +// Writes CBOR-encoded structure to |credentialData| containing |docType|, +// |testCredential| and |credentialKeys|. The latter element will be stored in +// encrypted form, using |hardwareBoundKey| as the encryption key. +bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType, + bool testCredential, const vector<uint8_t>& credentialKeys, + vector<uint8_t>& credentialData) { + optional<vector<uint8_t>> nonce = support::getRandom(12); + if (!nonce) { + LOG(ERROR) << "Error getting random"; + return false; + } + vector<uint8_t> docTypeAsVec(docType.begin(), docType.end()); + optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm( + hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec); + if (!credentialBlob) { + LOG(ERROR) << "Error encrypting CredentialKeys blob"; + return false; + } + + cppbor::Array array; + array.add(docType); + array.add(testCredential); + array.add(cppbor::Bstr(credentialBlob.value())); + credentialData = array.encode(); + return true; } -Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries( + vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) { if (signedDataCurrentNamespace_.size() > 0) { signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_)); } @@ -412,29 +395,27 @@ Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries {}, // additionalData {}); // certificateChain if (!signature) { - _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error signing data")); } vector<uint8_t> credentialKeys; if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) { - _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {}); - return Void(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys")); } vector<uint8_t> credentialData; - if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey() - : support::getHardwareBoundKey(), - docType_, testCredential_, credentialKeys, credentialData)) { - _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {}); - return Void(); + if (!generateCredentialData( + testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(), + docType_, testCredential_, credentialKeys, credentialData)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData")); } - _hidl_cb(support::resultOK(), credentialData, signature.value()); - return Void(); + *outCredentialData = byteStringToSigned(credentialData); + *outProofOfProvisioningSignature = byteStringToSigned(signature.value()); + return ndk::ScopedAStatus::ok(); } -} // namespace implementation -} // namespace identity -} // namespace hardware -} // namespace android +} // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h new file mode 100644 index 0000000000..b380f897a1 --- /dev/null +++ b/identity/aidl/default/WritableIdentityCredential.h @@ -0,0 +1,90 @@ +/* + * Copyright 2019, 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. + */ + +#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H +#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H + +#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h> +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <cppbor.h> + +namespace aidl::android::hardware::identity { + +using ::std::string; +using ::std::vector; + +class WritableIdentityCredential : public BnWritableIdentityCredential { + public: + WritableIdentityCredential(const string& docType, bool testCredential) + : docType_(docType), testCredential_(testCredential) {} + + // Creates the Credential Key. Returns false on failure. Must be called + // right after construction. + bool initialize(); + + // Methods from IWritableIdentityCredential follow. + ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId, + const vector<int8_t>& attestationChallenge, + vector<Certificate>* outCertificateChain) override; + + ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount, + const vector<int32_t>& entryCounts) override; + + ndk::ScopedAStatus addAccessControlProfile( + int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired, + int64_t timeoutMillis, int64_t secureUserId, + SecureAccessControlProfile* outSecureAccessControlProfile) override; + + ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds, + const string& nameSpace, const string& name, + int32_t entrySize) override; + + ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content, + vector<int8_t>* outEncryptedContent) override; + + ndk::ScopedAStatus finishAddingEntries( + vector<int8_t>* outCredentialData, + vector<int8_t>* outProofOfProvisioningSignature) override; + + // private: + string docType_; + bool testCredential_; + + // These are set in initialize(). + vector<uint8_t> storageKey_; + vector<uint8_t> credentialPrivKey_; + vector<uint8_t> credentialPubKey_; + + // These fields are initialized during startPersonalization() + size_t numAccessControlProfileRemaining_; + vector<int32_t> remainingEntryCounts_; + cppbor::Array signedDataAccessControlProfiles_; + cppbor::Map signedDataNamespaces_; + cppbor::Array signedDataCurrentNamespace_; + + // These fields are initialized during beginAddEntry() + size_t entryRemainingBytes_; + vector<uint8_t> entryAdditionalData_; + string entryNameSpace_; + string entryName_; + vector<int32_t> entryAccessControlProfileIds_; + vector<uint8_t> entryBytes_; +}; + +} // namespace aidl::android::hardware::identity + +#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H diff --git a/identity/aidl/default/identity-default.rc b/identity/aidl/default/identity-default.rc new file mode 100644 index 0000000000..d3b62c1042 --- /dev/null +++ b/identity/aidl/default/identity-default.rc @@ -0,0 +1,3 @@ +service vendor.identity-default /vendor/bin/hw/android.hardware.identity-service.example + class hal + user nobody diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml new file mode 100644 index 0000000000..a47d354ce5 --- /dev/null +++ b/identity/aidl/default/identity-default.xml @@ -0,0 +1,9 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.identity</name> + <interface> + <name>IIdentityCredentialStore</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp new file mode 100644 index 0000000000..f05c615001 --- /dev/null +++ b/identity/aidl/default/service.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2019, 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. + */ + +#define LOG_TAG "android.hardware.identity-service" + +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "IdentityCredentialStore.h" + +using aidl::android::hardware::identity::IdentityCredentialStore; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr<IdentityCredentialStore> store = + ndk::SharedRefBase::make<IdentityCredentialStore>(); + + const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default"; + LOG(INFO) << "instance: " << instance; + binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp new file mode 100644 index 0000000000..21ff440075 --- /dev/null +++ b/identity/aidl/vts/Android.bp @@ -0,0 +1,21 @@ +cc_test { + name: "VtsHalIdentityTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: ["VtsHalIdentityTargetTest.cpp"], + shared_libs: [ + "libbinder", + "libcppbor", + "android.hardware.identity-support-lib", + ], + static_libs: [ + "android.hardware.identity-cpp", + "android.hardware.keymaster-cpp", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp index 88b06df046..5abe5a2f66 100644 --- a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp +++ b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp @@ -13,62 +13,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "VtsHalIdentityTargetTest" -#define LOG_TAG "IdentityCredentialHidlHalTest" - -#include <map> - +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> #include <android-base/logging.h> -#include <android/hardware/identity/1.0/IIdentityCredentialStore.h> -#include <android/hardware/identity/1.0/types.h> +#include <android/hardware/identity/IIdentityCredentialStore.h> #include <android/hardware/identity/support/IdentityCredentialSupport.h> - +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <cppbor.h> #include <cppbor_parse.h> #include <gtest/gtest.h> -#include <hidl/GtestPrinter.h> -#include <hidl/ServiceManagement.h> +#include <future> +#include <map> + +namespace android::hardware::identity { using std::map; using std::optional; using std::string; using std::vector; -namespace android { -namespace hardware { -namespace identity { -namespace test { +using ::android::sp; +using ::android::String16; +using ::android::binder::Status; -using ::android::hardware::identity::V1_0::IIdentityCredential; -using ::android::hardware::identity::V1_0::IIdentityCredentialStore; -using ::android::hardware::identity::V1_0::IWritableIdentityCredential; -using ::android::hardware::identity::V1_0::Result; -using ::android::hardware::identity::V1_0::ResultCode; -using ::android::hardware::identity::V1_0::SecureAccessControlProfile; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; +using ::android::hardware::keymaster::HardwareAuthToken; // --------------------------------------------------------------------------- // Test Data. // --------------------------------------------------------------------------- struct TestEntryData { - TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds) + TestEntryData(string nameSpace, string name, vector<int32_t> profileIds) : nameSpace(nameSpace), name(name), profileIds(profileIds) {} - TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds) + TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Tstr(((const char*)value.data())).encode(); } TestEntryData(string nameSpace, string name, const vector<uint8_t>& value, - vector<uint16_t> profileIds) + vector<int32_t> profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Bstr(value).encode(); } - TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds) + TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Bool(value).encode(); } - TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds) + TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds) : TestEntryData(nameSpace, name, profileIds) { if (value >= 0) { valueCbor = cppbor::Uint(value).encode(); @@ -80,63 +74,38 @@ struct TestEntryData { string nameSpace; string name; vector<uint8_t> valueCbor; - vector<uint16_t> profileIds; + vector<int32_t> profileIds; }; struct TestProfile { uint16_t id; - hidl_vec<uint8_t> readerCertificate; + vector<uint8_t> readerCertificate; bool userAuthenticationRequired; uint64_t timeoutMillis; }; -/************************************ - * TEST DATA FOR AUTHENTICATION - ************************************/ -// Test authentication token for user authentication +// ---------------------------------------------------------------- -class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> { +class IdentityAidl : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { - string serviceName = GetParam(); - ASSERT_FALSE(serviceName.empty()); - credentialStore_ = IIdentityCredentialStore::getService(serviceName); + credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>( + String16(GetParam().c_str())); ASSERT_NE(credentialStore_, nullptr); - - credentialStore_->getHardwareInformation( - [&](const Result& result, const hidl_string& credentialStoreName, - const hidl_string& credentialStoreAuthorName, uint32_t chunkSize, - bool /* isDirectAccess */, - const hidl_vec<hidl_string> /* supportedDocTypes */) { - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); - ASSERT_GT(credentialStoreName.size(), 0u); - ASSERT_GT(credentialStoreAuthorName.size(), 0u); - ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported - dataChunkSize_ = chunkSize; - }); } - virtual void TearDown() override {} - - uint32_t dataChunkSize_ = 0; sp<IIdentityCredentialStore> credentialStore_; }; -TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) { - credentialStore_->getHardwareInformation( - [&](const Result& result, const hidl_string& credentialStoreName, - const hidl_string& credentialStoreAuthorName, uint32_t chunkSize, - bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) { - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); - ASSERT_GT(credentialStoreName.size(), 0u); - ASSERT_GT(credentialStoreAuthorName.size(), 0u); - ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported - }); +TEST_P(IdentityAidl, hardwareInformation) { + HardwareInformation info; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk()); + ASSERT_GT(info.credentialStoreName.size(), 0); + ASSERT_GT(info.credentialStoreAuthorName.size(), 0); + ASSERT_GE(info.dataChunkSize, 256); } -TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { +TEST_P(IdentityAidl, createAndRetrieveCredential) { // First, generate a key-pair for the reader since its public key will be // part of the request data. optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair(); @@ -168,75 +137,59 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { // Profile 1 (no authentication) {1, {}, false, 0}}; - HardwareAuthToken authToken = {}; + HardwareAuthToken authToken; // Here's the actual test data: const vector<TestEntryData> testEntries = { - {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}}, - {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}}, - {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}}, + {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}}, + {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}}, + {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}}, {"PersonalData", "Home address", string("Maida Vale, London, England"), - vector<uint16_t>{0}}, - {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}}, + vector<int32_t>{0}}, + {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}}, }; - const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1), - 1u}; + const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1), + 1u}; + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); string cborPretty; sp<IWritableIdentityCredential> writableCredential; - - hidl_vec<uint8_t> empty{0}; - string docType = "org.iso.18013-5.2019.mdl"; bool testCredential = true; - Result result; - credentialStore_->createCredential( - docType, testCredential, - [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) { - result = _result; - writableCredential = _writableCredential; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential) + .isOk()); ASSERT_NE(writableCredential, nullptr); string challenge = "attestationChallenge"; // TODO: set it to something random and check it's in the cert chain vector<uint8_t> attestationApplicationId = {}; vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); - vector<uint8_t> attestationCertificate; - writableCredential->getAttestationCertificate( - attestationApplicationId, attestationChallenge, - [&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) { - result = _result; - vector<vector<uint8_t>> splitCerts; - std::copy(_splitCertChain.begin(), _splitCertChain.end(), - std::back_inserter(splitCerts)); - attestationCertificate = support::certificateChainJoin(splitCerts); - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); - - writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts, - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + vector<Certificate> attestationCertificates; + ASSERT_TRUE(writableCredential + ->getAttestationCertificate(attestationApplicationId, attestationChallenge, + &attestationCertificates) + .isOk()); + ASSERT_GE(attestationCertificates.size(), 2); + + ASSERT_TRUE( + writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts) + .isOk()); vector<SecureAccessControlProfile> returnedSecureProfiles; for (const auto& testProfile : testProfiles) { SecureAccessControlProfile profile; - writableCredential->addAccessControlProfile( - testProfile.id, testProfile.readerCertificate, - testProfile.userAuthenticationRequired, testProfile.timeoutMillis, - 0, // secureUserId - [&](const Result& _result, const SecureAccessControlProfile& _profile) { - result = _result; - profile = _profile; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + Certificate cert; + cert.encodedCertificate = testProfile.readerCertificate; + ASSERT_TRUE(writableCredential + ->addAccessControlProfile(testProfile.id, cert, + testProfile.userAuthenticationRequired, + testProfile.timeoutMillis, + 0, // secureUserId + &profile) + .isOk()); ASSERT_EQ(testProfile.id, profile.id); - ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate); + ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate); ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired); ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis); ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size()); @@ -248,38 +201,28 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs; for (const auto& entry : testEntries) { - vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_); + vector<vector<uint8_t>> chunks = + support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize); - writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name, - entry.valueCbor.size(), - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(writableCredential + ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name, + entry.valueCbor.size()) + .isOk()); vector<vector<uint8_t>> encryptedChunks; for (const auto& chunk : chunks) { - writableCredential->addEntryValue( - chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) { - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); - ASSERT_GT(encryptedContent.size(), 0u); - encryptedChunks.push_back(encryptedContent); - }); + vector<uint8_t> encryptedChunk; + ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk()); + encryptedChunks.push_back(encryptedChunk); } encryptedBlobs[&entry] = encryptedChunks; } vector<uint8_t> credentialData; vector<uint8_t> proofOfProvisioningSignature; - writableCredential->finishAddingEntries( - [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData, - const hidl_vec<uint8_t>& _proofOfProvisioningSignature) { - result = _result; - credentialData = _credentialData; - proofOfProvisioningSignature = _proofOfProvisioningSignature; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE( + writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature) + .isOk()); optional<vector<uint8_t>> proofOfProvisioning = support::coseSignGetPayload(proofOfProvisioningSignature); @@ -334,7 +277,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { cborPretty); optional<vector<uint8_t>> credentialPubKey = - support::certificateChainGetTopMostKey(attestationCertificate); + support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate); ASSERT_TRUE(credentialPubKey); EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature, {}, // Additional data @@ -344,32 +287,21 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { // Now that the credential has been provisioned, read it back and check the // correct data is returned. sp<IIdentityCredential> credential; - credentialStore_->getCredential( - credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) { - result = _result; - credential = _credential; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credentialStore_ + ->getCredential( + CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256, + credentialData, &credential) + .isOk()); ASSERT_NE(credential, nullptr); optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair(); ASSERT_TRUE(readerEphemeralKeyPair); optional<vector<uint8_t>> readerEphemeralPublicKey = support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value()); - credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(), - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk()); vector<uint8_t> ephemeralKeyPair; - credential->createEphemeralKeyPair( - [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) { - result = _result; - ephemeralKeyPair = _ephemeralKeyPair; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk()); optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair); // Calculate requestData field and sign it with the reader key. @@ -420,19 +352,17 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { readerCertificate.value()); ASSERT_TRUE(readerSignature); - credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes, - sessionTranscriptBytes, readerSignature.value(), - testEntriesEntryCounts, - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential + ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes, + sessionTranscriptBytes, readerSignature.value(), + testEntriesEntryCounts) + .isOk()); for (const auto& entry : testEntries) { - credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(), - entry.profileIds, - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential + ->startRetrieveEntryValue(entry.nameSpace, entry.name, + entry.valueCbor.size(), entry.profileIds) + .isOk()); auto it = encryptedBlobs.find(&entry); ASSERT_NE(it, encryptedBlobs.end()); @@ -441,13 +371,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { vector<uint8_t> content; for (const auto& encryptedChunk : encryptedChunks) { vector<uint8_t> chunk; - credential->retrieveEntryValue( - encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) { - result = _result; - chunk = _chunk; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk()); content.insert(content.end(), chunk.begin(), chunk.end()); } EXPECT_EQ(content, entry.valueCbor); @@ -455,28 +379,12 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { // Generate the key that will be used to sign AuthenticatedData. vector<uint8_t> signingKeyBlob; - vector<uint8_t> signingKeyCertificate; - credential->generateSigningKeyPair([&](const Result& _result, - const hidl_vec<uint8_t> _signingKeyBlob, - const hidl_vec<uint8_t> _signingKeyCertificate) { - result = _result; - signingKeyBlob = _signingKeyBlob; - signingKeyCertificate = _signingKeyCertificate; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + Certificate signingKeyCertificate; + ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk()); vector<uint8_t> mac; vector<uint8_t> deviceNameSpacesBytes; - credential->finishRetrieval(signingKeyBlob, - [&](const Result& _result, const hidl_vec<uint8_t> _mac, - const hidl_vec<uint8_t> _deviceNameSpacesBytes) { - result = _result; - mac = _mac; - deviceNameSpacesBytes = _deviceNameSpacesBytes; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk()); cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {}); ASSERT_EQ( "{\n" @@ -501,7 +409,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes)); vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode(); optional<vector<uint8_t>> signingPublicKey = - support::certificateChainGetTopMostKey(signingKeyCertificate); + support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate); EXPECT_TRUE(signingPublicKey); // Derive the key used for MACing. @@ -521,12 +429,18 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { EXPECT_EQ(mac, calculatedMac); } -INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest, - testing::ValuesIn(android::hardware::getAllHalInstanceNames( - IIdentityCredentialStore::descriptor)), - android::hardware::PrintInstanceNameToString); +INSTANTIATE_TEST_SUITE_P( + Identity, IdentityAidl, + testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)), + android::PrintInstanceNameToString); +// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl, +// testing::Values("android.hardware.identity.IIdentityCredentialStore/default")); + +} // namespace android::hardware::identity -} // namespace test -} // namespace identity -} // namespace hardware -} // namespace android +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1); + ::android::ProcessState::self()->startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/identity/support/Android.bp b/identity/support/Android.bp index 38dc10b485..7b4546b20f 100644 --- a/identity/support/Android.bp +++ b/identity/support/Android.bp @@ -23,7 +23,6 @@ cc_library { "include", ], shared_libs: [ - "android.hardware.identity@1.0", "libcrypto", "libbase", "libhidlbase", @@ -41,7 +40,6 @@ cc_test { ], shared_libs: [ "android.hardware.identity-support-lib", - "android.hardware.identity@1.0", "libcrypto", "libbase", "libhidlbase", diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h index 485571a775..4533ad946c 100644 --- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h @@ -18,12 +18,11 @@ #define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_ #include <cstdint> +#include <optional> #include <string> #include <tuple> #include <vector> -#include <android/hardware/identity/1.0/types.h> - namespace android { namespace hardware { namespace identity { @@ -34,10 +33,6 @@ using ::std::string; using ::std::tuple; using ::std::vector; -using ::android::hardware::identity::V1_0::Result; -using ::android::hardware::identity::V1_0::ResultCode; -using ::android::hardware::identity::V1_0::SecureAccessControlProfile; - // --------------------------------------------------------------------------- // Miscellaneous utilities. // --------------------------------------------------------------------------- @@ -258,21 +253,11 @@ optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint const vector<uint8_t>& detachedContent); // --------------------------------------------------------------------------- -// Platform abstraction. -// --------------------------------------------------------------------------- - -// Returns the hardware-bound AES-128 key. -const vector<uint8_t>& getHardwareBoundKey(); - -// --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- -// Returns a reference to a Result with code OK and empty message. -const Result& resultOK(); - -// Returns a new Result with the given code and message. -Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3))); +// Returns the testing AES-128 key where all bits are set to 0. +const vector<uint8_t>& getTestHardwareBoundKey(); // Splits the given bytestring into chunks. If the given vector is smaller or equal to // |maxChunkSize| a vector with |content| as the only element is returned. Otherwise @@ -280,21 +265,6 @@ Result result(ResultCode code, const char* format, ...) __attribute__((format(pr // may be smaller than |maxChunkSize|. vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize); -// Calculates the MAC for |profile| using |storageKey|. -optional<vector<uint8_t>> secureAccessControlProfileCalcMac( - const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey); - -// Checks authenticity of the MAC in |profile| using |storageKey|. -bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, - const vector<uint8_t>& storageKey); - -// Returns the testing AES-128 key where all bits are set to 0. -const vector<uint8_t>& getTestHardwareBoundKey(); - -// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method. -vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name, - const vector<uint16_t> accessControlProfileIds); - } // namespace support } // namespace identity } // namespace hardware diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp index 7d93a4b737..e2828bf2bd 100644 --- a/identity/support/src/IdentityCredentialSupport.cpp +++ b/identity/support/src/IdentityCredentialSupport.cpp @@ -1682,36 +1682,9 @@ optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint } // --------------------------------------------------------------------------- -// Platform abstraction. -// --------------------------------------------------------------------------- - -// This is not a very random HBK but that's OK because this is the SW -// implementation where it can't be kept secret. -vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - -const vector<uint8_t>& getHardwareBoundKey() { - return hardwareBoundKey; -} - -// --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- -Result okResult{ResultCode::OK, ""}; - -const Result& resultOK() { - return okResult; -} - -Result result(ResultCode code, const char* format, ...) { - va_list ap; - va_start(ap, format); - string str; - android::base::StringAppendV(&str, format, ap); - va_end(ap); - return Result{code, str}; -} - vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) { vector<vector<uint8_t>> ret; @@ -1738,56 +1711,6 @@ vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxCh return ret; } -vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) { - cppbor::Map map; - map.add("id", profile.id); - - if (profile.readerCertificate.size() > 0) { - map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate)); - } - - if (profile.userAuthenticationRequired) { - map.add("userAuthenticationRequired", profile.userAuthenticationRequired); - map.add("timeoutMillis", profile.timeoutMillis); - map.add("secureUserId", profile.secureUserId); - } - - return map.encode(); -} - -optional<vector<uint8_t>> secureAccessControlProfileCalcMac( - const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) { - vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile); - - optional<vector<uint8_t>> nonce = getRandom(12); - if (!nonce) { - return {}; - } - optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData); - if (!macO) { - return {}; - } - return macO.value(); -} - -bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, - const vector<uint8_t>& storageKey) { - vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile); - - if (profile.mac.size() < kAesGcmIvSize) { - return false; - } - vector<uint8_t> nonce = - vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize); - optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData); - if (!mac) { - return false; - } - if (mac.value() != vector<uint8_t>(profile.mac)) { - return false; - } - return true; -} vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -1795,20 +1718,6 @@ const vector<uint8_t>& getTestHardwareBoundKey() { return testHardwareBoundKey; } -vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name, - const vector<uint16_t> accessControlProfileIds) { - cppbor::Map map; - map.add("Namespace", nameSpace); - map.add("Name", name); - - cppbor::Array acpIds; - for (auto id : accessControlProfileIds) { - acpIds.add(id); - } - map.add("AccessControlProfileIds", std::move(acpIds)); - return map.encode(); -} - } // namespace support } // namespace identity } // namespace hardware |