From 81603155a9582941cf558e840ffff0c90edaf330 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 11 Feb 2020 22:04:24 -0500 Subject: [PATCH] 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 --- identity/1.0/Android.bp | 20 - identity/1.0/default/Android.bp | 43 -- identity/1.0/default/IdentityCredential.h | 132 ---- .../1.0/default/IdentityCredentialStore.cpp | 68 -- .../1.0/default/IdentityCredentialStore.h | 55 -- .../1.0/default/WritableIdentityCredential.h | 106 --- ...d.hardware.identity@1.0-service.example.rc | 3 - identity/1.0/default/service.cpp | 40 -- identity/1.0/types.hal | 164 ----- identity/1.0/vts/OWNERS | 2 - identity/1.0/vts/functional/Android.bp | 36 - identity/{1.0/default => }/OWNERS | 0 identity/aidl/Android.bp | 21 + .../hardware/identity/Certificate.aidl | 27 + .../hardware/identity/CipherSuite.aidl | 39 ++ .../identity/HardwareInformation.aidl | 54 ++ .../identity/IIdentityCredential.aidl} | 119 ++-- .../identity/IIdentityCredentialStore.aidl} | 140 ++-- .../IWritableIdentityCredential.aidl} | 98 ++- .../identity/SecureAccessControlProfile.aidl | 78 +++ identity/aidl/default/Android.bp | 29 + .../default/IdentityCredential.cpp | 479 +++++++------ identity/aidl/default/IdentityCredential.h | 109 +++ .../aidl/default/IdentityCredentialStore.cpp | 74 ++ .../aidl/default/IdentityCredentialStore.h | 48 ++ identity/aidl/default/Util.cpp | 117 ++++ identity/aidl/default/Util.h | 58 ++ .../default/WritableIdentityCredential.cpp | 663 +++++++++--------- .../aidl/default/WritableIdentityCredential.h | 90 +++ identity/aidl/default/identity-default.rc | 3 + identity/aidl/default/identity-default.xml | 9 + identity/aidl/default/service.cpp | 39 ++ identity/aidl/vts/Android.bp | 21 + .../vts/VtsHalIdentityTargetTest.cpp} | 308 +++----- identity/support/Android.bp | 2 - .../support/IdentityCredentialSupport.h | 36 +- .../support/src/IdentityCredentialSupport.cpp | 91 --- keymaster/aidl/Android.bp | 18 + .../hardware/keymaster/HardwareAuthToken.aidl | 89 +++ .../keymaster/HardwareAuthenticatorType.aidl | 32 + .../android/hardware/keymaster/Timestamp.aidl | 22 + 41 files changed, 1833 insertions(+), 1749 deletions(-) delete mode 100644 identity/1.0/Android.bp delete mode 100644 identity/1.0/default/Android.bp delete mode 100644 identity/1.0/default/IdentityCredential.h delete mode 100644 identity/1.0/default/IdentityCredentialStore.cpp delete mode 100644 identity/1.0/default/IdentityCredentialStore.h delete mode 100644 identity/1.0/default/WritableIdentityCredential.h delete mode 100644 identity/1.0/default/android.hardware.identity@1.0-service.example.rc delete mode 100644 identity/1.0/default/service.cpp delete mode 100644 identity/1.0/types.hal delete mode 100644 identity/1.0/vts/OWNERS delete mode 100644 identity/1.0/vts/functional/Android.bp rename identity/{1.0/default => }/OWNERS (100%) create mode 100644 identity/aidl/Android.bp create mode 100644 identity/aidl/android/hardware/identity/Certificate.aidl create mode 100644 identity/aidl/android/hardware/identity/CipherSuite.aidl create mode 100644 identity/aidl/android/hardware/identity/HardwareInformation.aidl rename identity/{1.0/IIdentityCredential.hal => aidl/android/hardware/identity/IIdentityCredential.aidl} (76%) rename identity/{1.0/IIdentityCredentialStore.hal => aidl/android/hardware/identity/IIdentityCredentialStore.aidl} (68%) rename identity/{1.0/IWritableIdentityCredential.hal => aidl/android/hardware/identity/IWritableIdentityCredential.aidl} (76%) create mode 100644 identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl create mode 100644 identity/aidl/default/Android.bp rename identity/{1.0 => aidl}/default/IdentityCredential.cpp (57%) create mode 100644 identity/aidl/default/IdentityCredential.h create mode 100644 identity/aidl/default/IdentityCredentialStore.cpp create mode 100644 identity/aidl/default/IdentityCredentialStore.h create mode 100644 identity/aidl/default/Util.cpp create mode 100644 identity/aidl/default/Util.h rename identity/{1.0 => aidl}/default/WritableIdentityCredential.cpp (61%) create mode 100644 identity/aidl/default/WritableIdentityCredential.h create mode 100644 identity/aidl/default/identity-default.rc create mode 100644 identity/aidl/default/identity-default.xml create mode 100644 identity/aidl/default/service.cpp create mode 100644 identity/aidl/vts/Android.bp rename identity/{1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp => aidl/vts/VtsHalIdentityTargetTest.cpp} (58%) create mode 100644 keymaster/aidl/Android.bp create mode 100644 keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl create mode 100644 keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl create mode 100644 keymaster/aidl/android/hardware/keymaster/Timestamp.aidl 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 - -#include - -#include -#include -#include -#include - -#include - -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>; - -class IdentityCredential : public IIdentityCredential { - public: - IdentityCredential(const hidl_vec& 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 deleteCredential(deleteCredential_cb _hidl_cb) override; - Return createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override; - - Return setReaderEphemeralPublicKey(const hidl_vec& publicKey, - setReaderEphemeralPublicKey_cb _hidl_cb) override; - - Return createAuthChallenge(createAuthChallenge_cb _hidl_cb) override; - - Return startRetrieval(const hidl_vec& accessControlProfiles, - const HardwareAuthToken& authToken, - const hidl_vec& itemsRequest, - const hidl_vec& sessionTranscript, - const hidl_vec& readerSignature, - const hidl_vec& requestCounts, - startRetrieval_cb _hidl_cb) override; - Return startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name, - uint32_t entrySize, - const hidl_vec& accessControlProfileIds, - startRetrieveEntryValue_cb _hidl_cb) override; - Return retrieveEntryValue(const hidl_vec& encryptedContent, - retrieveEntryValue_cb _hidl_cb) override; - Return finishRetrieval(const hidl_vec& signingKeyBlob, - finishRetrieval_cb _hidl_cb) override; - - Return generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override; - - private: - // Set by constructor - vector credentialData_; - int numStartRetrievalCalls_; - - // Set by initialize() - string docType_; - bool testCredential_; - vector storageKey_; - vector credentialPrivKey_; - - // Set by createEphemeralKeyPair() - vector ephemeralPublicKey_; - - // Set by setReaderEphemeralPublicKey() - vector readerPublicKey_; - - // Set by createAuthChallenge() - uint64_t authChallenge_; - - // Set at startRetrieval() time. - map profileIdToAccessCheckResult_; - vector sessionTranscript_; - std::unique_ptr sessionTranscriptItem_; - vector itemsRequest_; - vector requestCountsRemaining_; - MapStringToVectorOfStrings requestedNameSpacesAndNames_; - cppbor::Map deviceNameSpacesMap_; - cppbor::Map currentNameSpaceDeviceNameSpacesMap_; - - // Set at startRetrieveEntryValue() time. - string currentNameSpace_; - string currentName_; - size_t entryRemainingBytes_; - vector entryValue_; - vector 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 - -namespace android { -namespace hardware { -namespace identity { -namespace implementation { - -// Methods from ::android::hardware::identity::IIdentityCredentialStore follow. - -Return IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) { - _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google", - kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */); - return Void(); -} - -Return 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 IdentityCredentialStore::getCredential(const hidl_vec& 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 - -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 getHardwareInformation(getHardwareInformation_cb _hidl_cb) override; - Return createCredential(const hidl_string& docType, bool testCredential, - createCredential_cb _hidl_cb) override; - Return getCredential(const hidl_vec& 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 - -#include - -#include - -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 getAttestationCertificate(const hidl_vec& attestationApplicationId, - const hidl_vec& attestationChallenge, - getAttestationCertificate_cb _hidl_cb) override; - - Return startPersonalization(uint16_t accessControlProfileCount, - const hidl_vec& entryCounts, - startPersonalization_cb _hidl_cb) override; - - Return addAccessControlProfile(uint16_t id, const hidl_vec& readerCertificate, - bool userAuthenticationRequired, uint64_t timeoutMillis, - uint64_t secureUserId, - addAccessControlProfile_cb _hidl_cb) override; - - Return beginAddEntry(const hidl_vec& accessControlProfileIds, - const hidl_string& nameSpace, const hidl_string& name, - uint32_t entrySize, beginAddEntry_cb _hidl_cb) override; - - Return addEntryValue(const hidl_vec& content, - addEntryValue_cb _hidl_cb) override; - - Return finishAddingEntries(finishAddingEntries_cb _hidl_cb) override; - - private: - string docType_; - bool testCredential_; - - // These are set in initialize(). - vector storageKey_; - vector credentialPrivKey_; - vector credentialPubKey_; - - // These fields are initialized during startPersonalization() - size_t numAccessControlProfileRemaining_; - vector remainingEntryCounts_; - cppbor::Array signedDataAccessControlProfiles_; - cppbor::Map signedDataNamespaces_; - cppbor::Array signedDataCurrentNamespace_; - - // These fields are initialized during beginAddEntry() - size_t entryRemainingBytes_; - vector entryAdditionalData_; - string entryNameSpace_; - string entryName_; - vector entryAccessControlProfileIds_; - vector 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 -#include - -#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 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 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 similarity index 100% rename from identity/1.0/default/OWNERS rename to 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 similarity index 76% rename from identity/1.0/IIdentityCredential.hal rename to 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 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 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 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 accessControlProfiles, - HardwareAuthToken authToken, - vec itemsRequest, - vec sessionTranscript, - vec readerSignature, - vec 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 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 encryptedContent) - generates (Result result, vec 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. + * is not in the right format the call fails with STATUS_INVALID_DATA. * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. - * - * @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 signingKeyBlob) - generates(Result result, vec mac, vec 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. + * @param out signingKeyBlob contains an encrypted copy of the newly-generated private + * signing key. * - * @return 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 signingKeyBlob, - vec signingKeyCertificate); -}; + Certificate generateSigningKeyPair(out byte[] signingKeyBlob); +} diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl similarity index 68% rename from identity/1.0/IIdentityCredentialStore.hal rename to 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 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 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 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 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 similarity index 76% rename from identity/1.0/IWritableIdentityCredential.hal rename to 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 attestationApplicationId, - vec attestationChallenge) - generates(Result result, vec> 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 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. + * STATUS_INVALID_DATA. * - * @return result is OK on success, INVALID_DATA or FAILED if an error occurred. - * - * @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 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 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 content) - generates(Result result, vec 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 credentialData, - vec 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 similarity index 57% rename from identity/1.0/default/IdentityCredential.cpp rename to 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 @@ -28,71 +29,24 @@ #include #include -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 IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) { - cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_}; - vector proofOfDeletion = array.encode(); +using namespace ::android::hardware::identity; - optional> 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 IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) { - optional> 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> 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(); -} - -Return IdentityCredential::setReaderEphemeralPublicKey( - const hidl_vec& 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& 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 IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::deleteCredential( + vector* outProofOfDeletionSignature) { + cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_}; + vector proofOfDeletion = array.encode(); + + optional> 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* outKeyPair) { + optional> 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> 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& publicKey) { + readerPublicKey_ = byteStringToUnsigned(publicKey); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) { uint64_t challenge = 0; while (challenge == 0) { optional> 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 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& readerCertificateChain) { - optional> acpPubKey = - support::certificateChainGetTopMostKey(profile.readerCertificate); + optional> 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 IdentityCredential::startRetrieval( - const hidl_vec& accessControlProfiles, - const HardwareAuthToken& authToken, const hidl_vec& itemsRequest, - const hidl_vec& sessionTranscript, const hidl_vec& readerSignature, - const hidl_vec& requestCounts, startRetrieval_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::startRetrieval( + const vector& accessControlProfiles, + const HardwareAuthToken& authToken, const vector& itemsRequestS, + const vector& sessionTranscriptS, const vector& readerSignatureS, + const vector& 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(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 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> 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& itemsRequestBytes = itemsRequest; @@ -313,9 +314,9 @@ Return 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 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& 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 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 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 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 requestedKeys; @@ -458,9 +459,9 @@ Return 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 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 IdentityCredential::startRetrieval( itemsRequest_ = itemsRequest; numStartRetrievalCalls_ += 1; - _hidl_cb(support::resultOK()); - return Void(); + return ndk::ScopedAStatus::ok(); } -Return IdentityCredential::startRetrieveEntryValue( - const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize, - const hidl_vec& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( + const string& nameSpace, const string& name, int32_t entrySize, + const vector& 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 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 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 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 IdentityCredential::retrieveEntryValue(const hidl_vec& encryptedContent, - retrieveEntryValue_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector& encryptedContentS, + vector* outContent) { + auto encryptedContent = byteStringToUnsigned(encryptedContentS); + optional> 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 IdentityCredential::retrieveEntryValue(const hidl_vec& 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 IdentityCredential::retrieveEntryValue(const hidl_vec& 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 IdentityCredential::finishRetrieval(const hidl_vec& signingKeyBlob, - finishRetrieval_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector& signingKeyBlobS, + vector* outMac, + vector* outDeviceNameSpaces) { + auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS); + if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) { deviceNameSpacesMap_.add(currentNameSpace_, std::move(currentNameSpaceDeviceNameSpacesMap_)); @@ -682,40 +676,42 @@ Return IdentityCredential::finishRetrieval(const hidl_vec& signin optional> 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> 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 salt = {0x00}; vector info = {}; optional> 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({})), encodedDeviceNameSpaces); - return Void(); + *outMac = byteStringToSigned(mac.value_or(vector({}))); + *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces); + return ndk::ScopedAStatus::ok(); } -Return IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) { +ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair( + vector* 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 IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_c optional> 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> 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> 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> 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> 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 docTypeAsBlob(docType_.begin(), docType_.end()); optional> 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 +#include +#include + +#include +#include +#include +#include + +#include + +namespace aidl::android::hardware::identity { + +using ::aidl::android::hardware::keymaster::HardwareAuthToken; +using ::std::map; +using ::std::string; +using ::std::vector; + +using MapStringToVectorOfStrings = map>; + +class IdentityCredential : public BnIdentityCredential { + public: + IdentityCredential(const vector& 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* outProofOfDeletionSignature) override; + ndk::ScopedAStatus createEphemeralKeyPair(vector* outKeyPair) override; + ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector& publicKey) override; + ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override; + ndk::ScopedAStatus startRetrieval( + const vector& accessControlProfiles, + const HardwareAuthToken& authToken, const vector& itemsRequest, + const vector& sessionTranscript, const vector& readerSignature, + const vector& requestCounts) override; + ndk::ScopedAStatus startRetrieveEntryValue( + const string& nameSpace, const string& name, int32_t entrySize, + const vector& accessControlProfileIds) override; + ndk::ScopedAStatus retrieveEntryValue(const vector& encryptedContent, + vector* outContent) override; + ndk::ScopedAStatus finishRetrieval(const vector& signingKeyBlob, vector* outMac, + vector* outDeviceNameSpaces) override; + ndk::ScopedAStatus generateSigningKeyPair(vector* outSigningKeyBlob, + Certificate* outSigningKeyCertificate) override; + + private: + // Set by constructor + vector credentialData_; + int numStartRetrievalCalls_; + + // Set by initialize() + string docType_; + bool testCredential_; + vector storageKey_; + vector credentialPrivKey_; + + // Set by createEphemeralKeyPair() + vector ephemeralPublicKey_; + + // Set by setReaderEphemeralPublicKey() + vector readerPublicKey_; + + // Set by createAuthChallenge() + uint64_t authChallenge_; + + // Set at startRetrieval() time. + map profileIdToAccessCheckResult_; + vector sessionTranscript_; + std::unique_ptr sessionTranscriptItem_; + vector itemsRequest_; + vector requestCountsRemaining_; + MapStringToVectorOfStrings requestedNameSpacesAndNames_; + cppbor::Map deviceNameSpacesMap_; + cppbor::Map currentNameSpaceDeviceNameSpacesMap_; + + // Set at startRetrieveEntryValue() time. + string currentNameSpace_; + string currentName_; + size_t entryRemainingBytes_; + vector entryValue_; + vector 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 + +#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* outWritableCredential) { + shared_ptr wc = + ndk::SharedRefBase::make(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& credentialData, + shared_ptr* 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 data = vector(credentialData.begin(), credentialData.end()); + shared_ptr credential = ndk::SharedRefBase::make(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 + +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* outWritableCredential) override; + + ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector& credentialData, + shared_ptr* 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 + +#include + +#include + +#include +#include + +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 hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +const vector& getHardwareBoundKey() { + return hardwareBoundKey; +} + +vector byteStringToUnsigned(const vector& value) { + return vector(value.begin(), value.end()); +} + +vector byteStringToSigned(const vector& value) { + return vector(value.begin(), value.end()); +} + +vector 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> secureAccessControlProfileCalcMac( + const SecureAccessControlProfile& profile, const vector& storageKey) { + vector cborData = secureAccessControlProfileEncodeCbor(profile); + + optional> nonce = support::getRandom(12); + if (!nonce) { + return {}; + } + optional> macO = + support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData); + if (!macO) { + return {}; + } + return macO.value(); +} + +bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, + const vector& storageKey) { + vector cborData = secureAccessControlProfileEncodeCbor(profile); + + if (profile.mac.size() < support::kAesGcmIvSize) { + return false; + } + vector nonce = + vector(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize); + optional> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData); + if (!mac) { + return false; + } + if (mac.value() != byteStringToUnsigned(profile.mac)) { + return false; + } + return true; +} + +vector entryCreateAdditionalData(const string& nameSpace, const string& name, + const vector 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 +#include + +#include +#include +#include +#include +#include + +#include + +namespace aidl::android::hardware::identity { + +using ::std::optional; +using ::std::string; +using ::std::vector; + +// Returns the hardware-bound AES-128 key. +const vector& getHardwareBoundKey(); + +// Calculates the MAC for |profile| using |storageKey|. +optional> secureAccessControlProfileCalcMac( + const SecureAccessControlProfile& profile, const vector& storageKey); + +// Checks authenticity of the MAC in |profile| using |storageKey|. +bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, + const vector& storageKey); + +// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method. +vector entryCreateAdditionalData(const string& nameSpace, const string& name, + const vector accessControlProfileIds); + +vector byteStringToUnsigned(const vector& value); + +vector byteStringToSigned(const vector& 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 similarity index 61% rename from identity/1.0/default/WritableIdentityCredential.cpp rename to 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 #include @@ -26,12 +23,315 @@ #include #include -namespace android { -namespace hardware { -namespace identity { -namespace implementation { +#include "IdentityCredentialStore.h" +#include "Util.h" +#include "WritableIdentityCredential.h" + +namespace aidl::android::hardware::identity { using ::std::optional; +using namespace ::android::hardware::identity; + +bool WritableIdentityCredential::initialize() { + optional> keyPair = support::createEcKeyPair(); + if (!keyPair) { + LOG(ERROR) << "Error creating credentialKey"; + return false; + } + + optional> pubKey = support::ecKeyPairGetPublicKey(keyPair.value()); + if (!pubKey) { + LOG(ERROR) << "Error getting public part of credentialKey"; + return false; + } + credentialPubKey_ = pubKey.value(); + + optional> privKey = support::ecKeyPairGetPrivateKey(keyPair.value()); + if (!privKey) { + LOG(ERROR) << "Error getting private part of credentialKey"; + return false; + } + credentialPrivKey_ = privKey.value(); + + optional> random = support::getRandom(16); + if (!random) { + LOG(ERROR) << "Error creating storageKey"; + return false; + } + storageKey_ = random.value(); + + return true; +} + +// TODO: use |attestationApplicationId| and |attestationChallenge| and also +// ensure the returned certificate chain satisfy the requirements listed in +// the docs for IWritableIdentityCredential::getAttestationCertificate() +// +ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate( + const vector& /*attestationApplicationId*/, + const vector& /*attestationChallenge*/, vector* 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> attestationKeyPair = support::createEcKeyPair(); + if (!attestationKeyPair) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey")); + } + + optional> attestationPubKey = + support::ecKeyPairGetPublicKey(attestationKeyPair.value()); + if (!attestationPubKey) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting public part of attestationKey")); + } + + optional> attestationPrivKey = + support::ecKeyPairGetPrivateKey(attestationKeyPair.value()); + if (!attestationPrivKey) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error getting private part of attestationKey")); + } + + string serialDecimal; + string issuer; + string subject; + time_t validityNotBefore = time(nullptr); + time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600; + + // First create a certificate for |credentialPubKey| which is signed by + // |attestationPrivKey|. + // + serialDecimal = "0"; // TODO: set serial to |attestationChallenge| + issuer = "Android Open Source Project"; + subject = "Android IdentityCredential CredentialKey"; + optional> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate( + credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject, + validityNotBefore, validityNotAfter); + if (!credentialPubKeyCertificate) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error creating certificate for credentialPubKey")); + } + + // This is followed by a certificate for |attestationPubKey| self-signed by + // |attestationPrivKey|. + serialDecimal = "0"; // TODO: set serial + issuer = "Android Open Source Project"; + subject = "Android IdentityCredential AttestationKey"; + optional> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate( + attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject, + validityNotBefore, validityNotAfter); + if (!attestationKeyCertificate) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error creating certificate for attestationPubKey")); + } + + // Concatenate the certificates to form the chain. + vector certificateChain; + certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(), + credentialPubKeyCertificate.value().end()); + certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(), + attestationKeyCertificate.value().end()); + + optional>> splitCertChain = + support::certificateChainSplit(certificateChain); + if (!splitCertChain) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain")); + } + *outCertificateChain = vector(); + for (const vector& cert : splitCertChain.value()) { + Certificate c = Certificate(); + c.encodedCertificate = byteStringToSigned(cert); + outCertificateChain->push_back(std::move(c)); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus WritableIdentityCredential::startPersonalization( + int32_t accessControlProfileCount, const vector& entryCounts) { + numAccessControlProfileRemaining_ = accessControlProfileCount; + remainingEntryCounts_ = entryCounts; + entryNameSpace_ = ""; + + signedDataAccessControlProfiles_ = cppbor::Array(); + signedDataNamespaces_ = cppbor::Map(); + signedDataCurrentNamespace_ = cppbor::Array(); + + return ndk::ScopedAStatus::ok(); +} + +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) { + 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) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "userAuthenticationRequired is false but timeout is non-zero")); + } + + profile.id = id; + profile.readerCertificate = readerCertificate; + profile.userAuthenticationRequired = userAuthenticationRequired; + profile.timeoutMillis = timeoutMillis; + profile.secureUserId = secureUserId; + optional> mac = secureAccessControlProfileCalcMac(profile, storageKey_); + if (!mac) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile")); + } + profile.mac = byteStringToSigned(mac.value()); + + cppbor::Map profileMap; + profileMap.add("id", profile.id); + if (profile.readerCertificate.encodedCertificate.size() > 0) { + profileMap.add( + "readerCertificate", + cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate))); + } + if (profile.userAuthenticationRequired) { + profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired); + profileMap.add("timeoutMillis", profile.timeoutMillis); + } + signedDataAccessControlProfiles_.add(std::move(profileMap)); + + numAccessControlProfileRemaining_--; + + *outSecureAccessControlProfile = profile; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry( + const vector& accessControlProfileIds, const string& nameSpace, const string& name, + int32_t entrySize) { + if (numAccessControlProfileRemaining_ != 0) { + LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_ + << " and expected zero"; + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "numAccessControlProfileRemaining_ is not zero")); + } + + if (remainingEntryCounts_.size() == 0) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to")); + } + + // Handle initial beginEntry() call. + if (entryNameSpace_ == "") { + entryNameSpace_ = nameSpace; + } + + // If the namespace changed... + if (nameSpace != entryNameSpace_) { + // Then check that all entries in the previous namespace have been added.. + if (remainingEntryCounts_[0] != 0) { + 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()); + + if (signedDataCurrentNamespace_.size() > 0) { + signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_)); + signedDataCurrentNamespace_ = cppbor::Array(); + } + } else { + // Same namespace... + if (remainingEntryCounts_[0] == 0) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Same namespace but no entries remain to be added")); + } + remainingEntryCounts_[0] -= 1; + } + + entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); + + entryRemainingBytes_ = entrySize; + entryNameSpace_ = nameSpace; + entryName_ = name; + entryAccessControlProfileIds_ = accessControlProfileIds; + entryBytes_.resize(0); + // LOG(INFO) << "name=" << name << " entrySize=" << entrySize; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector& contentS, + vector* outEncryptedContent) { + auto content = byteStringToUnsigned(contentS); + size_t contentSize = content.size(); + + if (contentSize > IdentityCredentialStore::kGcmChunkSize) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Passed in chunk of is bigger than kGcmChunkSize")); + } + if (contentSize > entryRemainingBytes_) { + 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) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + "Retrieved non-final chunk which isn't kGcmChunkSize")); + } + } + + optional> nonce = support::getRandom(12); + if (!nonce) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce")); + } + optional> encryptedContent = + support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_); + if (!encryptedContent) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content")); + } + + if (entryRemainingBytes_ == 0) { + // TODO: ideally do do this without parsing the data (but still validate data is valid + // CBOR). + auto [item, _, message] = cppbor::parse(entryBytes_); + if (item == nullptr) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR")); + } + cppbor::Map entryMap; + entryMap.add("name", entryName_); + entryMap.add("value", std::move(item)); + cppbor::Array profileIdArray; + for (auto id : entryAccessControlProfileIds_) { + profileIdArray.add(id); + } + entryMap.add("accessControlProfiles", std::move(profileIdArray)); + signedDataCurrentNamespace_.add(std::move(entryMap)); + } + + *outEncryptedContent = byteStringToSigned(encryptedContent.value()); + return ndk::ScopedAStatus::ok(); +} // Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and // |credentialPrivKey|. @@ -77,325 +377,8 @@ bool generateCredentialData(const vector& hardwareBoundKey, const strin return true; } -bool WritableIdentityCredential::initialize() { - optional> keyPair = support::createEcKeyPair(); - if (!keyPair) { - LOG(ERROR) << "Error creating credentialKey"; - return false; - } - - optional> pubKey = support::ecKeyPairGetPublicKey(keyPair.value()); - if (!pubKey) { - LOG(ERROR) << "Error getting public part of credentialKey"; - return false; - } - credentialPubKey_ = pubKey.value(); - - optional> privKey = support::ecKeyPairGetPrivateKey(keyPair.value()); - if (!privKey) { - LOG(ERROR) << "Error getting private part of credentialKey"; - return false; - } - credentialPrivKey_ = privKey.value(); - - optional> random = support::getRandom(16); - if (!random) { - LOG(ERROR) << "Error creating storageKey"; - return false; - } - storageKey_ = random.value(); - - return true; -} - -// TODO: use |attestationApplicationId| and |attestationChallenge| and also -// ensure the returned certificate chain satisfy the requirements listed in -// the docs for IWritableIdentityCredential::getAttestationCertificate() -// -Return WritableIdentityCredential::getAttestationCertificate( - const hidl_vec& /* attestationApplicationId */, - const hidl_vec& /* attestationChallenge */, - getAttestationCertificate_cb _hidl_cb) { - // 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> attestationKeyPair = support::createEcKeyPair(); - if (!attestationKeyPair) { - _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {}); - return Void(); - } - - optional> attestationPubKey = - support::ecKeyPairGetPublicKey(attestationKeyPair.value()); - if (!attestationPubKey) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"), - {}); - return Void(); - } - - optional> attestationPrivKey = - support::ecKeyPairGetPrivateKey(attestationKeyPair.value()); - if (!attestationPrivKey) { - _hidl_cb( - support::result(ResultCode::FAILED, "Error getting private part of attestationKey"), - {}); - return Void(); - } - - string serialDecimal; - string issuer; - string subject; - time_t validityNotBefore = time(nullptr); - time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600; - - // First create a certificate for |credentialPubKey| which is signed by - // |attestationPrivKey|. - // - serialDecimal = "0"; // TODO: set serial to |attestationChallenge| - issuer = "Android Open Source Project"; - subject = "Android IdentityCredential CredentialKey"; - optional> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate( - credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject, - validityNotBefore, validityNotAfter); - if (!credentialPubKeyCertificate) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error creating certificate for credentialPubKey"), - {}); - return Void(); - } - - // This is followed by a certificate for |attestationPubKey| self-signed by - // |attestationPrivKey|. - serialDecimal = "0"; // TODO: set serial - issuer = "Android Open Source Project"; - subject = "Android IdentityCredential AttestationKey"; - optional> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate( - attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject, - validityNotBefore, validityNotAfter); - if (!attestationKeyCertificate) { - _hidl_cb(support::result(ResultCode::FAILED, - "Error creating certificate for attestationPubKey"), - {}); - return Void(); - } - - // Concatenate the certificates to form the chain. - vector certificateChain; - certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(), - credentialPubKeyCertificate.value().end()); - certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(), - attestationKeyCertificate.value().end()); - - optional>> splitCertChain = - support::certificateChainSplit(certificateChain); - if (!splitCertChain) { - _hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {}); - return Void(); - } - hidl_vec> ret; - ret.resize(splitCertChain.value().size()); - std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin()); - _hidl_cb(support::resultOK(), ret); - return Void(); -} - -Return WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount, - const hidl_vec& entryCounts, - startPersonalization_cb _hidl_cb) { - numAccessControlProfileRemaining_ = accessControlProfileCount; - remainingEntryCounts_ = entryCounts; - entryNameSpace_ = ""; - - signedDataAccessControlProfiles_ = cppbor::Array(); - signedDataNamespaces_ = cppbor::Map(); - signedDataCurrentNamespace_ = cppbor::Array(); - - _hidl_cb(support::resultOK()); - return Void(); -} - -Return WritableIdentityCredential::addAccessControlProfile( - uint16_t id, const hidl_vec& readerCertificate, bool userAuthenticationRequired, - uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) { - SecureAccessControlProfile profile; - - if (numAccessControlProfileRemaining_ == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, - "numAccessControlProfileRemaining_ is 0 and expected non-zero"), - profile); - return Void(); - } - - // 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(); - } - - profile.id = id; - profile.readerCertificate = readerCertificate; - profile.userAuthenticationRequired = userAuthenticationRequired; - profile.timeoutMillis = timeoutMillis; - profile.secureUserId = secureUserId; - optional> mac = - support::secureAccessControlProfileCalcMac(profile, storageKey_); - if (!mac) { - _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile); - return Void(); - } - profile.mac = mac.value(); - - cppbor::Map profileMap; - profileMap.add("id", profile.id); - if (profile.readerCertificate.size() > 0) { - profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate)); - } - if (profile.userAuthenticationRequired) { - profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired); - profileMap.add("timeoutMillis", profile.timeoutMillis); - } - signedDataAccessControlProfiles_.add(std::move(profileMap)); - - numAccessControlProfileRemaining_--; - - _hidl_cb(support::resultOK(), profile); - return Void(); -} - -Return WritableIdentityCredential::beginAddEntry( - const hidl_vec& accessControlProfileIds, const hidl_string& nameSpace, - const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) { - 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(); - } - - if (remainingEntryCounts_.size() == 0) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to")); - return Void(); - } - - // Handle initial beginEntry() call. - if (entryNameSpace_ == "") { - entryNameSpace_ = nameSpace; - } - - // If the namespace changed... - 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(); - } - remainingEntryCounts_.erase(remainingEntryCounts_.begin()); - - if (signedDataCurrentNamespace_.size() > 0) { - signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_)); - signedDataCurrentNamespace_ = cppbor::Array(); - } - } 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(); - } - remainingEntryCounts_[0] -= 1; - } - - entryAdditionalData_ = - support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds); - - entryRemainingBytes_ = entrySize; - entryNameSpace_ = nameSpace; - entryName_ = name; - entryAccessControlProfileIds_ = accessControlProfileIds; - entryBytes_.resize(0); - // LOG(INFO) << "name=" << name << " entrySize=" << entrySize; - - _hidl_cb(support::resultOK()); - return Void(); -} - -Return WritableIdentityCredential::addEntryValue(const hidl_vec& content, - addEntryValue_cb _hidl_cb) { - 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(); - } - 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(); - } - - 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(); - } - } - - optional> nonce = support::getRandom(12); - if (!nonce) { - _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {}); - return Void(); - } - optional> encryptedContent = - support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_); - if (!encryptedContent) { - _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {}); - return Void(); - } - - if (entryRemainingBytes_ == 0) { - // TODO: ideally do do this without parsing the data (but still validate data is valid - // CBOR). - auto [item, _, message] = cppbor::parse(entryBytes_); - if (item == nullptr) { - _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {}); - return Void(); - } - cppbor::Map entryMap; - entryMap.add("name", entryName_); - entryMap.add("value", std::move(item)); - cppbor::Array profileIdArray; - for (auto id : entryAccessControlProfileIds_) { - profileIdArray.add(id); - } - entryMap.add("accessControlProfiles", std::move(profileIdArray)); - signedDataCurrentNamespace_.add(std::move(entryMap)); - } - - _hidl_cb(support::resultOK(), encryptedContent.value()); - return Void(); -} - -Return WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) { +ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries( + vector* outCredentialData, vector* outProofOfProvisioningSignature) { if (signedDataCurrentNamespace_.size() > 0) { signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_)); } @@ -412,29 +395,27 @@ Return 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 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 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 +#include + +#include + +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& attestationApplicationId, + const vector& attestationChallenge, + vector* outCertificateChain) override; + + ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount, + const vector& 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& accessControlProfileIds, + const string& nameSpace, const string& name, + int32_t entrySize) override; + + ndk::ScopedAStatus addEntryValue(const vector& content, + vector* outEncryptedContent) override; + + ndk::ScopedAStatus finishAddingEntries( + vector* outCredentialData, + vector* outProofOfProvisioningSignature) override; + + // private: + string docType_; + bool testCredential_; + + // These are set in initialize(). + vector storageKey_; + vector credentialPrivKey_; + vector credentialPubKey_; + + // These fields are initialized during startPersonalization() + size_t numAccessControlProfileRemaining_; + vector remainingEntryCounts_; + cppbor::Array signedDataAccessControlProfiles_; + cppbor::Map signedDataNamespaces_; + cppbor::Array signedDataCurrentNamespace_; + + // These fields are initialized during beginAddEntry() + size_t entryRemainingBytes_; + vector entryAdditionalData_; + string entryNameSpace_; + string entryName_; + vector entryAccessControlProfileIds_; + vector 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 @@ + + + android.hardware.identity + + IIdentityCredentialStore + default + + + 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 +#include +#include + +#include "IdentityCredentialStore.h" + +using aidl::android::hardware::identity::IdentityCredentialStore; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr store = + ndk::SharedRefBase::make(); + + 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 similarity index 58% rename from identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp rename to 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 - +#include +#include #include -#include -#include +#include #include - +#include +#include #include #include #include -#include -#include +#include +#include + +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 profileIds) + TestEntryData(string nameSpace, string name, vector profileIds) : nameSpace(nameSpace), name(name), profileIds(profileIds) {} - TestEntryData(string nameSpace, string name, const string& value, vector profileIds) + TestEntryData(string nameSpace, string name, const string& value, vector profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Tstr(((const char*)value.data())).encode(); } TestEntryData(string nameSpace, string name, const vector& value, - vector profileIds) + vector profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Bstr(value).encode(); } - TestEntryData(string nameSpace, string name, bool value, vector profileIds) + TestEntryData(string nameSpace, string name, bool value, vector profileIds) : TestEntryData(nameSpace, name, profileIds) { valueCbor = cppbor::Bool(value).encode(); } - TestEntryData(string nameSpace, string name, int64_t value, vector profileIds) + TestEntryData(string nameSpace, string name, int64_t value, vector profileIds) : TestEntryData(nameSpace, name, profileIds) { if (value >= 0) { valueCbor = cppbor::Uint(value).encode(); @@ -80,63 +74,38 @@ struct TestEntryData { string nameSpace; string name; vector valueCbor; - vector profileIds; + vector profileIds; }; struct TestProfile { uint16_t id; - hidl_vec readerCertificate; + vector readerCertificate; bool userAuthenticationRequired; uint64_t timeoutMillis; }; -/************************************ - * TEST DATA FOR AUTHENTICATION - ************************************/ -// Test authentication token for user authentication +// ---------------------------------------------------------------- -class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam { +class IdentityAidl : public testing::TestWithParam { public: virtual void SetUp() override { - string serviceName = GetParam(); - ASSERT_FALSE(serviceName.empty()); - credentialStore_ = IIdentityCredentialStore::getService(serviceName); + credentialStore_ = android::waitForDeclaredService( + 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 /* 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 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 /* 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> 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 testEntries = { - {"PersonalData", "Last name", string("Turing"), vector{0, 1}}, - {"PersonalData", "Birth date", string("19120623"), vector{0, 1}}, - {"PersonalData", "First name", string("Alan"), vector{0, 1}}, + {"PersonalData", "Last name", string("Turing"), vector{0, 1}}, + {"PersonalData", "Birth date", string("19120623"), vector{0, 1}}, + {"PersonalData", "First name", string("Alan"), vector{0, 1}}, {"PersonalData", "Home address", string("Maida Vale, London, England"), - vector{0}}, - {"Image", "Portrait image", portraitImage, vector{0, 1}}, + vector{0}}, + {"Image", "Portrait image", portraitImage, vector{0, 1}}, }; - const vector testEntriesEntryCounts = {static_cast(testEntries.size() - 1), - 1u}; + const vector testEntriesEntryCounts = {static_cast(testEntries.size() - 1), + 1u}; + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); string cborPretty; sp writableCredential; - - hidl_vec empty{0}; - string docType = "org.iso.18013-5.2019.mdl"; bool testCredential = true; - Result result; - credentialStore_->createCredential( - docType, testCredential, - [&](const Result& _result, const sp& _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 attestationApplicationId = {}; vector attestationChallenge(challenge.begin(), challenge.end()); - vector attestationCertificate; - writableCredential->getAttestationCertificate( - attestationApplicationId, attestationChallenge, - [&](const Result& _result, const hidl_vec>& _splitCertChain) { - result = _result; - vector> 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); + vector attestationCertificates; + ASSERT_TRUE(writableCredential + ->getAttestationCertificate(attestationApplicationId, attestationChallenge, + &attestationCertificates) + .isOk()); + ASSERT_GE(attestationCertificates.size(), 2); - writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts, - [&](const Result& _result) { result = _result; }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE( + writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts) + .isOk()); vector 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>> encryptedBlobs; for (const auto& entry : testEntries) { - vector> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_); + vector> 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> encryptedChunks; for (const auto& chunk : chunks) { - writableCredential->addEntryValue( - chunk, [&](const Result& result, hidl_vec encryptedContent) { - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); - ASSERT_GT(encryptedContent.size(), 0u); - encryptedChunks.push_back(encryptedContent); - }); + vector encryptedChunk; + ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk()); + encryptedChunks.push_back(encryptedChunk); } encryptedBlobs[&entry] = encryptedChunks; } vector credentialData; vector proofOfProvisioningSignature; - writableCredential->finishAddingEntries( - [&](const Result& _result, const hidl_vec& _credentialData, - const hidl_vec& _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> proofOfProvisioning = support::coseSignGetPayload(proofOfProvisioningSignature); @@ -334,7 +277,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) { cborPretty); optional> 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 credential; - credentialStore_->getCredential( - credentialData, [&](const Result& _result, const sp& _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> readerEphemeralKeyPair = support::createEcKeyPair(); ASSERT_TRUE(readerEphemeralKeyPair); optional> 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 ephemeralKeyPair; - credential->createEphemeralKeyPair( - [&](const Result& _result, const hidl_vec& _ephemeralKeyPair) { - result = _result; - ephemeralKeyPair = _ephemeralKeyPair; - }); - EXPECT_EQ("", result.message); - ASSERT_EQ(ResultCode::OK, result.code); + ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk()); optional> 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 content; for (const auto& encryptedChunk : encryptedChunks) { vector chunk; - credential->retrieveEntryValue( - encryptedChunk, [&](const Result& _result, const hidl_vec& _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 signingKeyBlob; - vector signingKeyCertificate; - credential->generateSigningKeyPair([&](const Result& _result, - const hidl_vec _signingKeyBlob, - const hidl_vec _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 mac; vector deviceNameSpacesBytes; - credential->finishRetrieval(signingKeyBlob, - [&](const Result& _result, const hidl_vec _mac, - const hidl_vec _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 encodedDeviceAuthentication = deviceAuthentication.encode(); optional> 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 test -} // namespace identity -} // namespace hardware -} // namespace android +} // namespace android::hardware::identity + +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 +#include #include #include #include -#include - 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. // --------------------------------------------------------------------------- @@ -257,22 +252,12 @@ optional> coseSignGetX5Chain(const vector& signatureCos optional> coseMac0(const vector& key, const vector& data, const vector& detachedContent); -// --------------------------------------------------------------------------- -// Platform abstraction. -// --------------------------------------------------------------------------- - -// Returns the hardware-bound AES-128 key. -const vector& 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& 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> chunkVector(const vector& content, size_t maxChunkSize); -// Calculates the MAC for |profile| using |storageKey|. -optional> secureAccessControlProfileCalcMac( - const SecureAccessControlProfile& profile, const vector& storageKey); - -// Checks authenticity of the MAC in |profile| using |storageKey|. -bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, - const vector& storageKey); - -// Returns the testing AES-128 key where all bits are set to 0. -const vector& getTestHardwareBoundKey(); - -// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method. -vector entryCreateAdditionalData(const string& nameSpace, const string& name, - const vector 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 @@ -1681,37 +1681,10 @@ optional> coseMac0(const vector& key, const vector hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - -const vector& 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> chunkVector(const vector& content, size_t maxChunkSize) { vector> ret; @@ -1738,56 +1711,6 @@ vector> chunkVector(const vector& content, size_t maxCh return ret; } -vector 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> secureAccessControlProfileCalcMac( - const SecureAccessControlProfile& profile, const vector& storageKey) { - vector cborData = secureAccessControlProfileEncodeCbor(profile); - - optional> nonce = getRandom(12); - if (!nonce) { - return {}; - } - optional> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData); - if (!macO) { - return {}; - } - return macO.value(); -} - -bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile, - const vector& storageKey) { - vector cborData = secureAccessControlProfileEncodeCbor(profile); - - if (profile.mac.size() < kAesGcmIvSize) { - return false; - } - vector nonce = - vector(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize); - optional> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData); - if (!mac) { - return false; - } - if (mac.value() != vector(profile.mac)) { - return false; - } - return true; -} vector testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -1795,20 +1718,6 @@ const vector& getTestHardwareBoundKey() { return testHardwareBoundKey; } -vector entryCreateAdditionalData(const string& nameSpace, const string& name, - const vector 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 diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp new file mode 100644 index 0000000000..a2d73ead08 --- /dev/null +++ b/keymaster/aidl/Android.bp @@ -0,0 +1,18 @@ +aidl_interface { + name: "android.hardware.keymaster", + vendor_available: true, + srcs: [ + "android/hardware/keymaster/*.aidl", + ], + stability: "vintf", + backend: { + java: { + platform_apis: true, + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl new file mode 100644 index 0000000000..58602aaa49 --- /dev/null +++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl @@ -0,0 +1,89 @@ +/* + * 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.keymaster; + +import android.hardware.keymaster.Timestamp; +import android.hardware.keymaster.HardwareAuthenticatorType; + +/** + * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key. + * + * HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and + * Fingerprint, in response to successful user authentication events. These tokens are passed to + * begin(), update(), and finish() to prove that authentication occurred. See those methods for + * more details. It is up to the caller to determine which of the generated auth tokens is + * appropriate for a given key operation. + */ +@VintfStability +parcelable HardwareAuthToken { + + /** + * challenge is a value that's used to enable authentication tokens to authorize specific + * events. The primary use case for challenge is to authorize an IKeymasterDevice cryptographic + * operation, for keys that require authentication per operation. See begin() for details. + */ + long challenge; + + /** + * userId is the a "secure" user ID. It is not related to any Android user ID or UID, but is + * created in the Gatekeeper application in the secure environment. + */ + long userId; + + /** + * authenticatorId is the a "secure" user ID. It is not related to any Android user ID or UID, + * but is created in an authentication application in the secure environment, such as the + * Fingerprint application. + */ + long authenticatorId; // Secure authenticator ID. + + /** + * authenticatorType describes the type of authentication that took place, e.g. password or + * fingerprint. + */ + HardwareAuthenticatorType authenticatorType; + + /** + * timestamp indicates when the user authentication took place, in milliseconds since some + * starting point (generally the most recent device boot) which all of the applications within + * one secure environment must agree upon. This timestamp is used to determine whether or not + * the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT). + */ + Timestamp timestamp; + + /** + * MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0 + * and Fingerprint 1.0, as well as pre-treble HALs. + * + * The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows: + * + * HMAC_SHA256( + * H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp) + * + * where ``||'' represents concatenation, the leading zero is a single byte, and all integers + * are represented as unsigned values, the full width of the type. The challenge, userId and + * authenticatorId values are in machine order, but authenticatorType and timestamp are in + * network order (big-endian). This odd construction is compatible with the hw_auth_token_t + * structure, + * + * Note that mac is a vec rather than an array, not because it's actually variable-length but + * because it could be empty. As documented in the IKeymasterDevice::begin, + * IKeymasterDevice::update and IKeymasterDevice::finish doc comments, an empty mac indicates + * that this auth token is empty. + */ + byte[] mac; +} diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl new file mode 100644 index 0000000000..314185829f --- /dev/null +++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl @@ -0,0 +1,32 @@ +/* + * 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.keymaster; + +/** + * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to + * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for + * authenticating to activate a key. + */ +@VintfStability +@Backing(type="int") +enum HardwareAuthenticatorType { + NONE = 0, + PASSWORD = 1 << 0, + FINGERPRINT = 1 << 1, + // Additional entries must be powers of 2. + ANY = 0xFFFFFFFF, +} diff --git a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl new file mode 100644 index 0000000000..4b2f108887 --- /dev/null +++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl @@ -0,0 +1,22 @@ +/* + * 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.keymaster; + +@VintfStability +parcelable Timestamp { + long milliSeconds; +}