Merge "Confirmationui Rate Limiting App Abort Bug Fix"
diff --git a/identity/Android.bp b/identity/Android.bp
new file mode 100644
index 0000000..240e107
--- /dev/null
+++ b/identity/Android.bp
@@ -0,0 +1,86 @@
+cc_defaults {
+    name: "identity_defaults",
+    cflags: [
+        "-Wall",
+        // "-Werror",
+        "-Wextra",
+        "-Wunused",
+    ],
+    sanitize: {
+        misc_undefined : ["integer"],
+    },
+    clang : true,
+}
+
+cc_binary {
+    name: "credstore",
+    defaults: ["identity_defaults"],
+
+    srcs: [
+        "main.cpp",
+        "CredentialStore.cpp",
+        "CredentialStoreFactory.cpp",
+        "WritableCredential.cpp",
+        "Credential.cpp",
+        "CredentialData.cpp",
+        "Util.cpp",
+    ],
+    init_rc: ["credstore.rc"],
+    shared_libs: [
+        "android.hardware.identity@1.0",
+        "android.hardware.keymaster@4.0",
+        "libbase",
+        "libbinder",
+        "libkeystore_aidl",
+        "libcredstore_aidl",
+        "libutils",
+        "libhidlbase",
+        "android.hardware.identity-support-lib",
+        "libkeymaster4support",
+    ],
+    static_libs: [
+        "libcppbor",
+    ]
+}
+
+filegroup {
+    name: "credstore_aidl",
+    srcs: [
+        "binder/android/security/identity/ICredential.aidl",
+        "binder/android/security/identity/IWritableCredential.aidl",
+        "binder/android/security/identity/ICredentialStore.aidl",
+        "binder/android/security/identity/AccessControlProfileParcel.aidl",
+        "binder/android/security/identity/EntryNamespaceParcel.aidl",
+        "binder/android/security/identity/EntryParcel.aidl",
+        "binder/android/security/identity/RequestNamespaceParcel.aidl",
+        "binder/android/security/identity/RequestEntryParcel.aidl",
+        "binder/android/security/identity/ResultNamespaceParcel.aidl",
+        "binder/android/security/identity/ResultEntryParcel.aidl",
+        "binder/android/security/identity/GetEntriesResultParcel.aidl",
+        "binder/android/security/identity/AuthKeyParcel.aidl",
+        "binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
+        "binder/android/security/identity/ICredentialStoreFactory.aidl",
+    ],
+    path: "binder",
+}
+
+cc_library_shared {
+    name: "libcredstore_aidl",
+    srcs: [
+        ":credstore_aidl",
+        ],
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "system/security/identity/binder",
+        ],
+    },
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libkeymaster4support",
+    ],
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+}
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
new file mode 100644
index 0000000..6b03309
--- /dev/null
+++ b/identity/Credential.cpp
@@ -0,0 +1,494 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Credential"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android/security/identity/ICredentialStore.h>
+
+#include <android/security/keystore/IKeystoreService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include "Credential.h"
+#include "CredentialData.h"
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using std::optional;
+
+using android::security::keystore::IKeystoreService;
+
+using ::android::hardware::hidl_vec;
+
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+
+using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
+using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
+using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
+using ::android::hardware::identity::support::sha256;
+
+using android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+Credential::Credential(const std::string& dataPath, const std::string& credentialName)
+    : dataPath_(dataPath), credentialName_(credentialName) {}
+
+Credential::~Credential() {}
+
+Status Credential::loadCredential(sp<IIdentityCredentialStore> halStoreBinder) {
+    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+
+    data_ = data;
+
+    Result result;
+    sp<IIdentityCredential> halBinder;
+    halStoreBinder->getCredential(
+        data_->getCredentialData(),
+        [&](const Result& _result, const sp<IIdentityCredential>& _halBinder) {
+            result = _result;
+            halBinder = _halBinder;
+        });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "Error getting HAL binder";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+    }
+
+    halBinder_ = halBinder;
+
+    return Status::ok();
+}
+
+Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
+    *_aidl_return = data_->getAttestationCertificate();
+    return Status::ok();
+}
+
+// Returns operation handle
+Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) {
+
+    selectedAuthKey_ = data_->selectAuthKey(allowUsingExhaustedKeys);
+    if (selectedAuthKey_ == nullptr) {
+        return Status::fromServiceSpecificError(
+            ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+            "No suitable authentication key available");
+    }
+
+    Result result;
+    uint64_t challenge;
+    halBinder_->createAuthChallenge([&](const Result& _result, uint64_t _challenge) {
+        result = _result;
+        challenge = _challenge;
+    });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "createAuthChallenge() failed " << ((int)result.code) << ": "
+                   << result.message;
+        return halResultToGenericError(result);
+    }
+    if (challenge == 0) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Returned challenge is 0 (bug in HAL or TA)");
+    }
+
+    selectedChallenge_ = challenge;
+    *_aidl_return = challenge;
+    return Status::ok();
+}
+
+// Returns false if an error occurred communicating with keystore.
+//
+// Sets |authToken| to the empty vector if an auth token couldn't be obtained.
+//
+bool getAuthTokenFromKeystore(uint64_t challenge, uint64_t secureUserId,
+                              unsigned int authTokenMaxAgeMillis, vector<uint8_t>& authToken) {
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+    sp<IKeystoreService> keystore = interface_cast<IKeystoreService>(binder);
+    if (keystore == nullptr) {
+        return false;
+    }
+
+    vector<uint8_t> returnedAuthToken;
+    Status ret = keystore->getAuthTokenForCredstore(challenge, secureUserId, authTokenMaxAgeMillis,
+                                                    &returnedAuthToken);
+    if (!ret.isOk()) {
+        return false;
+    }
+    authToken = returnedAuthToken;
+    return true;
+}
+
+Status Credential::getEntries(const vector<uint8_t>& requestMessage,
+                              const vector<RequestNamespaceParcel>& requestNamespaces,
+                              const vector<uint8_t>& sessionTranscript,
+                              const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
+                              GetEntriesResultParcel* _aidl_return) {
+    GetEntriesResultParcel ret;
+
+    // Calculate requestCounts ahead of time and be careful not to include
+    // elements that don't exist.
+    //
+    // Also go through and figure out which access control profiles to include
+    // in the startRetrieval() call.
+    vector<uint16_t> requestCounts;
+    const vector<SecureAccessControlProfile>& allProfiles = data_->getSecureAccessControlProfiles();
+    vector<bool> includeProfile(allProfiles.size());
+    for (const RequestNamespaceParcel& rns : requestNamespaces) {
+        size_t numEntriesInNsToRequest = 0;
+        for (const RequestEntryParcel& rep : rns.entries) {
+            if (data_->hasEntryData(rns.namespaceName, rep.name)) {
+                numEntriesInNsToRequest++;
+            }
+
+            optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
+            if (data) {
+                for (uint16_t id : data.value().accessControlProfileIds) {
+                    if (id >= includeProfile.size()) {
+                        LOG(ERROR) << "Invalid accessControlProfileId " << id << " for "
+                                   << rns.namespaceName << ": " << rep.name;
+                        return Status::fromServiceSpecificError(
+                            ICredentialStore::ERROR_GENERIC, "Invalid accessProfileId for entry");
+                    }
+                    includeProfile[id] = true;
+                }
+            }
+        }
+        requestCounts.push_back(numEntriesInNsToRequest);
+    }
+
+    // Now that we know which profiles are needed, send only those to the
+    // HAL.
+    vector<SecureAccessControlProfile> selectedProfiles;
+    for (size_t n = 0; n < allProfiles.size(); n++) {
+        if (includeProfile[n]) {
+            selectedProfiles.push_back(allProfiles[n]);
+        }
+    }
+
+    // Calculate the highest [1] non-zero timeout and if user-auth is needed
+    // ... we need this to select an appropriate authToken.
+    //
+    // [1] : Why do we request the highest timeout and not the lowest? Well, we
+    //       return partial results in getEntries e.g. if some data elements
+    //       fail to authorize we'll still return the ones that did not fail. So
+    //       e.g. consider data elements A and B where A has an ACP with 60
+    //       seconds and B has an ACP with 3600 seconds. In this case we'll be
+    //       fine with getting an authToken for e.g. 2400 seconds which would
+    //       mean returning only B.
+    //
+    bool userAuthNeeded = false;
+    unsigned int authTokenMaxAgeMillis = 0;
+    for (auto& profile : selectedProfiles) {
+        if (profile.userAuthenticationRequired) {
+            userAuthNeeded = true;
+            if (profile.timeoutMillis > 0) {
+                if (profile.timeoutMillis > authTokenMaxAgeMillis) {
+                    authTokenMaxAgeMillis = profile.timeoutMillis;
+                }
+            }
+        }
+    }
+
+    // If requesting a challenge-based authToken the idea is that authentication
+    // happens as part of the transaction. As such, authTokenMaxAgeMillis should
+    // be nearly zero. We'll use 10 seconds for this.
+    if (userAuthNeeded && selectedChallenge_ != 0) {
+        authTokenMaxAgeMillis = 10 * 1000;
+    }
+
+    // Only get an authToken if it's actually needed.
+    HardwareAuthToken authToken;
+    if (userAuthNeeded) {
+        vector<uint8_t> authTokenBytes;
+        if (!getAuthTokenFromKeystore(selectedChallenge_, data_->getSecureUserId(),
+                                      authTokenMaxAgeMillis, authTokenBytes)) {
+            LOG(ERROR) << "Error getting auth token from keystore";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error getting auth token from keystore");
+        }
+        if (authTokenBytes.size() > 0) {
+            authToken =
+                android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(authTokenBytes);
+        }
+    }
+
+    Result result;
+    halBinder_->startRetrieval(selectedProfiles, authToken, requestMessage, sessionTranscript,
+                               readerSignature, requestCounts,
+                               [&](const Result& _result) { result = _result; });
+    if (result.code == ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
+        LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
+        return Status::fromServiceSpecificError(
+            ICredentialStore::ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, result.message.c_str());
+    } else if (result.code == ResultCode::READER_SIGNATURE_CHECK_FAILED) {
+        LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_INVALID_READER_SIGNATURE,
+                                                result.message.c_str());
+    } else if (result.code == ResultCode::INVALID_ITEMS_REQUEST_MESSAGE) {
+        LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
+        return Status::fromServiceSpecificError(
+            ICredentialStore::ERROR_INVALID_ITEMS_REQUEST_MESSAGE, result.message.c_str());
+    } else if (result.code == ResultCode::SESSION_TRANSCRIPT_MISMATCH) {
+        LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_SESSION_TRANSCRIPT_MISMATCH,
+                                                result.message.c_str());
+    } else if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
+        return halResultToGenericError(result);
+    }
+
+    for (const RequestNamespaceParcel& rns : requestNamespaces) {
+        ResultNamespaceParcel resultNamespaceParcel;
+        resultNamespaceParcel.namespaceName = rns.namespaceName;
+
+        for (const RequestEntryParcel& rep : rns.entries) {
+            ResultEntryParcel resultEntryParcel;
+            resultEntryParcel.name = rep.name;
+
+            optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
+            if (!data) {
+                resultEntryParcel.status = STATUS_NO_SUCH_ENTRY;
+                resultNamespaceParcel.entries.push_back(resultEntryParcel);
+                continue;
+            }
+
+            halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, data.value().size,
+                                                data.value().accessControlProfileIds,
+                                                [&](const Result& _result) { result = _result; });
+            if (result.code == ResultCode::USER_AUTHENTICATION_FAILED) {
+                resultEntryParcel.status = STATUS_USER_AUTHENTICATION_FAILED;
+                resultNamespaceParcel.entries.push_back(resultEntryParcel);
+                continue;
+            } else if (result.code == ResultCode::READER_AUTHENTICATION_FAILED) {
+                resultEntryParcel.status = STATUS_READER_AUTHENTICATION_FAILED;
+                resultNamespaceParcel.entries.push_back(resultEntryParcel);
+                continue;
+            } else if (result.code == ResultCode::NOT_IN_REQUEST_MESSAGE) {
+                resultEntryParcel.status = STATUS_NOT_IN_REQUEST_MESSAGE;
+                resultNamespaceParcel.entries.push_back(resultEntryParcel);
+                continue;
+            } else if (result.code == ResultCode::NO_ACCESS_CONTROL_PROFILES) {
+                resultEntryParcel.status = STATUS_NO_ACCESS_CONTROL_PROFILES;
+                resultNamespaceParcel.entries.push_back(resultEntryParcel);
+                continue;
+            } else if (result.code != ResultCode::OK) {
+                LOG(ERROR) << "startRetrieveEntryValue() failed " << ((int)result.code) << ": "
+                           << result.message;
+                return halResultToGenericError(result);
+            }
+
+            vector<uint8_t> value;
+            for (const auto& encryptedChunk : data.value().encryptedChunks) {
+                halBinder_->retrieveEntryValue(
+                    encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& chunk) {
+                        result = _result;
+                        value.insert(value.end(), chunk.begin(), chunk.end());
+                    });
+                if (result.code != ResultCode::OK) {
+                    LOG(ERROR) << "retrieveEntryValue failed() " << ((int)result.code) << ": "
+                               << result.message;
+                    return halResultToGenericError(result);
+                }
+            }
+
+            resultEntryParcel.status = STATUS_OK;
+            resultEntryParcel.value = value;
+            resultNamespaceParcel.entries.push_back(resultEntryParcel);
+        }
+        ret.resultNamespaces.push_back(resultNamespaceParcel);
+    }
+
+    // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
+    // the Java layer. So we could end up with no previously selected auth key and we may
+    // need one.
+    const AuthKeyData* authKey = selectedAuthKey_;
+    if (sessionTranscript.size() > 0) {
+        if (authKey == nullptr) {
+            authKey = data_->selectAuthKey(allowUsingExhaustedKeys);
+            if (authKey == nullptr) {
+                return Status::fromServiceSpecificError(
+                    ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+                    "No suitable authentication key available");
+            }
+        }
+    }
+
+    vector<uint8_t> signingKeyBlob;
+    if (authKey != nullptr) {
+        signingKeyBlob = authKey->keyBlob;
+    }
+    halBinder_->finishRetrieval(signingKeyBlob, [&](const Result& _result,
+                                                    const hidl_vec<uint8_t>& _mac,
+                                                    const hidl_vec<uint8_t>& _deviceNameSpaces) {
+        result = _result;
+        ret.mac = _mac;
+        ret.deviceNameSpaces = _deviceNameSpaces;
+        if (authKey != nullptr) {
+            ret.staticAuthenticationData = authKey->staticAuthenticationData;
+        }
+    });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "finishRetrieval failed() " << ((int)result.code) << ": " << result.message;
+        return halResultToGenericError(result);
+    }
+
+    // Ensure useCount is updated on disk.
+    if (authKey != nullptr) {
+        if (!data_->saveToDisk()) {
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error saving data");
+        }
+    }
+
+    *_aidl_return = ret;
+    return Status::ok();
+}
+
+Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
+    Result result;
+    halBinder_->deleteCredential(
+        [&](const Result& _result, const hidl_vec<uint8_t>& _proofOfDeletionSignature) {
+            result = _result;
+            *_aidl_return = _proofOfDeletionSignature;
+        });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "deleteCredential failed() " << ((int)result.code) << ": " << result.message;
+        return halResultToGenericError(result);
+    }
+    if (!data_->deleteCredential()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error deleting credential data on disk");
+    }
+    return Status::ok();
+}
+
+Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+    Result result;
+
+    vector<uint8_t> keyPair;
+    halBinder_->createEphemeralKeyPair(
+        [&](const Result& _result, const hidl_vec<uint8_t>& _keyPair) {
+            result = _result;
+            keyPair = _keyPair;
+        });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "createEphemeralKeyPair failed() " << ((int)result.code) << ": "
+                   << result.message;
+        return halResultToGenericError(result);
+    }
+
+    optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
+                                                               "ephemeralKey",  // Alias for key
+                                                               "0",  // Serial, as a decimal number
+                                                               "Credstore",      // Issuer
+                                                               "Ephemeral Key",  // Subject
+                                                               0,  // Validity Not Before
+                                                               24 * 60 * 60);  // Validity Not After
+    if (!pkcs12Bytes) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error creating PKCS#12 structure for key pair");
+    }
+    *_aidl_return = pkcs12Bytes.value();
+    return Status::ok();
+}
+
+Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+    Result result;
+    halBinder_->setReaderEphemeralPublicKey(publicKey,
+                                            [&](const Result& _result) { result = _result; });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "setReaderEphemeralPublicKey failed() " << ((int)result.code) << ": "
+                   << result.message;
+        return halResultToGenericError(result);
+    }
+    return Status::ok();
+}
+
+Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
+    data_->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+    if (!data_->saveToDisk()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error saving data");
+    }
+    return Status::ok();
+}
+
+Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+    optional<vector<vector<uint8_t>>> keysNeedingCert =
+        data_->getAuthKeysNeedingCertification(halBinder_);
+    if (!keysNeedingCert) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error getting auth keys neededing certification");
+    }
+    vector<AuthKeyParcel> authKeyParcels;
+    for (const vector<uint8_t>& key : keysNeedingCert.value()) {
+        AuthKeyParcel authKeyParcel;
+        authKeyParcel.x509cert = key;
+        authKeyParcels.push_back(authKeyParcel);
+    }
+    if (!data_->saveToDisk()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error saving data");
+    }
+    *_aidl_return = authKeyParcels;
+    return Status::ok();
+}
+
+Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
+                                                 const vector<uint8_t>& staticAuthData) {
+    if (!data_->storeStaticAuthenticationData(authenticationKey.x509cert, staticAuthData)) {
+        return Status::fromServiceSpecificError(
+            ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
+            "Error finding authentication key to store static "
+            "authentication data for");
+    }
+    if (!data_->saveToDisk()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error saving data");
+    }
+    return Status::ok();
+}
+
+Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
+    const vector<AuthKeyData>& authKeyDatas = data_->getAuthKeyDatas();
+    vector<int32_t> ret;
+    for (const AuthKeyData& authKeyData : authKeyDatas) {
+        ret.push_back(authKeyData.useCount);
+    }
+    *_aidl_return = ret;
+    return Status::ok();
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/Credential.h b/identity/Credential.h
new file mode 100644
index 0000000..356d75f
--- /dev/null
+++ b/identity/Credential.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_CREDENTIAL_H_
+#define SYSTEM_SECURITY_CREDENTIAL_H_
+
+#include <string>
+#include <vector>
+
+#include <android/security/identity/BnCredential.h>
+
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+#include <android/hardware/identity/1.0/types.h>
+
+#include "CredentialData.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::identity::V1_0::IIdentityCredential;
+using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
+
+class Credential : public BnCredential {
+  public:
+    Credential(const string& dataPath, const string& credentialName);
+    ~Credential();
+
+    Status loadCredential(sp<IIdentityCredentialStore> halStoreBinder);
+
+    // ICredential overrides
+    Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
+
+    Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
+
+    Status deleteCredential(vector<uint8_t>* _aidl_return) override;
+
+    Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
+
+    Status selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) override;
+
+    Status getEntries(const vector<uint8_t>& requestMessage,
+                      const vector<RequestNamespaceParcel>& requestNamespaces,
+                      const vector<uint8_t>& sessionTranscript,
+                      const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
+                      GetEntriesResultParcel* _aidl_return) override;
+
+    Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
+    Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
+    Status storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
+                                         const vector<uint8_t>& staticAuthData) override;
+    Status getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) override;
+
+  private:
+    string dataPath_;
+    string credentialName_;
+
+    const AuthKeyData* selectedAuthKey_ = nullptr;
+    uint64_t selectedChallenge_ = 0;
+
+    sp<CredentialData> data_;
+
+    sp<IIdentityCredential> halBinder_;
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_IDENTITY_CREDENTIAL_H_
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
new file mode 100644
index 0000000..99dd04b
--- /dev/null
+++ b/identity/CredentialData.cpp
@@ -0,0 +1,568 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CredentialData"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include "CredentialData.h"
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using android::hardware::identity::V1_0::Result;
+using android::hardware::identity::V1_0::ResultCode;
+using std::optional;
+
+string CredentialData::calculateCredentialFileName(const string& dataPath, uid_t ownerUid,
+                                                   const string& name) {
+    return android::base::StringPrintf(
+        "%s/%d-%s", dataPath.c_str(), (int)ownerUid,
+        android::hardware::identity::support::encodeHex(name).c_str());
+}
+
+CredentialData::CredentialData(const string& dataPath, uid_t ownerUid, const string& name)
+    : dataPath_(dataPath), ownerUid_(ownerUid), name_(name), secureUserId_(0) {
+    fileName_ = calculateCredentialFileName(dataPath_, ownerUid_, name_);
+}
+
+void CredentialData::setSecureUserId(int64_t secureUserId) {
+    secureUserId_ = secureUserId;
+}
+
+void CredentialData::setCredentialData(const vector<uint8_t>& credentialData) {
+    credentialData_ = credentialData;
+}
+
+void CredentialData::setAttestationCertificate(const vector<uint8_t>& attestationCertificate) {
+    attestationCertificate_ = attestationCertificate;
+}
+
+void CredentialData::addSecureAccessControlProfile(
+    const SecureAccessControlProfile& secureAccessControlProfile) {
+    secureAccessControlProfiles_.push_back(secureAccessControlProfile);
+}
+
+void CredentialData::addEntryData(const string& namespaceName, const string& entryName,
+                                  const EntryData& data) {
+    idToEncryptedChunks_[namespaceName + ":" + entryName] = data;
+}
+
+bool CredentialData::saveToDisk() const {
+    cppbor::Map map;
+
+    map.add("secureUserId", secureUserId_);
+
+    map.add("credentialData", credentialData_);
+
+    map.add("attestationCertificate", attestationCertificate_);
+
+    cppbor::Array sacpArray;
+    for (const SecureAccessControlProfile& sacp : secureAccessControlProfiles_) {
+        cppbor::Array array;
+        array.add(sacp.id);
+        array.add((const vector<uint8_t>&)sacp.readerCertificate);
+        array.add(sacp.userAuthenticationRequired);
+        array.add(sacp.timeoutMillis);
+        array.add(sacp.secureUserId);
+        vector<uint8_t> mac = sacp.mac;
+        array.add(mac);
+        sacpArray.add(std::move(array));
+    }
+    map.add("secureAccessControlProfiles", std::move(sacpArray));
+
+    cppbor::Map encryptedBlobsMap;
+    for (auto const& [nsAndName, entryData] : idToEncryptedChunks_) {
+        cppbor::Array encryptedChunkArray;
+        for (const vector<uint8_t>& encryptedChunk : entryData.encryptedChunks) {
+            encryptedChunkArray.add(encryptedChunk);
+        }
+        cppbor::Array entryDataArray;
+        entryDataArray.add(entryData.size);
+        cppbor::Array idsArray;
+        for (uint16_t id : entryData.accessControlProfileIds) {
+            idsArray.add(id);
+        }
+        entryDataArray.add(std::move(idsArray));
+        entryDataArray.add(std::move(encryptedChunkArray));
+        encryptedBlobsMap.add(nsAndName, std::move(entryDataArray));
+    }
+    map.add("entryData", std::move(encryptedBlobsMap));
+    map.add("authKeyCount", keyCount_);
+    map.add("maxUsesPerAuthKey", maxUsesPerKey_);
+
+    cppbor::Array authKeyDatasArray;
+    for (const AuthKeyData& data : authKeyDatas_) {
+        cppbor::Array array;
+        array.add(data.certificate);
+        array.add(data.keyBlob);
+        array.add(data.staticAuthenticationData);
+        array.add(data.pendingCertificate);
+        array.add(data.pendingKeyBlob);
+        array.add(data.useCount);
+        authKeyDatasArray.add(std::move(array));
+    }
+    map.add("authKeyData", std::move(authKeyDatasArray));
+
+    vector<uint8_t> credentialData = map.encode();
+
+    return fileSetContents(fileName_, credentialData);
+}
+
+optional<SecureAccessControlProfile> parseSacp(const cppbor::Item& item) {
+    const cppbor::Array* array = item.asArray();
+    if (array == nullptr || array->size() < 6) {
+        LOG(ERROR) << "The SACP CBOR is not an array with at least six elements (size="
+                   << (array != nullptr ? array->size() : -1) << ")";
+        return {};
+    }
+    const cppbor::Int* itemId = ((*array)[0])->asInt();
+    const cppbor::Bstr* itemReaderCertificate = ((*array)[1])->asBstr();
+    const cppbor::Simple* simple = ((*array)[2])->asSimple();
+    const cppbor::Bool* itemUserAuthenticationRequired =
+        (simple != nullptr ? (simple->asBool()) : nullptr);
+    const cppbor::Int* itemTimeoutMillis = ((*array)[3])->asInt();
+    const cppbor::Int* itesecureUserId_ = ((*array)[4])->asInt();
+    const cppbor::Bstr* itemMac = ((*array)[5])->asBstr();
+    if (itemId == nullptr || itemReaderCertificate == nullptr ||
+        itemUserAuthenticationRequired == nullptr || itemTimeoutMillis == nullptr ||
+        itesecureUserId_ == nullptr || itemMac == nullptr) {
+        LOG(ERROR) << "One or more items SACP array in CBOR is of wrong type";
+        return {};
+    }
+    SecureAccessControlProfile sacp;
+    sacp.id = itemId->value();
+    sacp.readerCertificate = itemReaderCertificate->value();
+    sacp.userAuthenticationRequired = itemUserAuthenticationRequired->value();
+    sacp.timeoutMillis = itemTimeoutMillis->value();
+    sacp.secureUserId = itesecureUserId_->value();
+    sacp.mac = itemMac->value();
+    return sacp;
+}
+
+optional<AuthKeyData> parseAuthKeyData(const cppbor::Item& item) {
+    const cppbor::Array* array = item.asArray();
+    if (array == nullptr || array->size() < 6) {
+        LOG(ERROR) << "The AuthKeyData CBOR is not an array with at least six elements";
+        return {};
+    }
+    const cppbor::Bstr* itemCertificate = ((*array)[0])->asBstr();
+    const cppbor::Bstr* itemKeyBlob = ((*array)[1])->asBstr();
+    const cppbor::Bstr* itemStaticAuthenticationData = ((*array)[2])->asBstr();
+    const cppbor::Bstr* itemPendingCertificate = ((*array)[3])->asBstr();
+    const cppbor::Bstr* itemPendingKeyBlob = ((*array)[4])->asBstr();
+    const cppbor::Int* itemUseCount = ((*array)[5])->asInt();
+    if (itemCertificate == nullptr || itemKeyBlob == nullptr ||
+        itemStaticAuthenticationData == nullptr || itemPendingCertificate == nullptr ||
+        itemPendingKeyBlob == nullptr || itemUseCount == nullptr) {
+        LOG(ERROR) << "One or more items in AuthKeyData array in CBOR is of wrong type";
+        return {};
+    }
+    AuthKeyData authKeyData;
+    authKeyData.certificate = itemCertificate->value();
+    authKeyData.keyBlob = itemKeyBlob->value();
+    authKeyData.staticAuthenticationData = itemStaticAuthenticationData->value();
+    authKeyData.pendingCertificate = itemPendingCertificate->value();
+    authKeyData.pendingKeyBlob = itemPendingKeyBlob->value();
+    authKeyData.useCount = itemUseCount->value();
+    return authKeyData;
+}
+
+vector<uint16_t> parseAccessControlProfileIds(const cppbor::Item& item) {
+    const cppbor::Array* array = item.asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "The accessControlProfileIds member is not an array";
+        return {};
+    }
+
+    vector<uint16_t> accessControlProfileIds;
+    for (size_t n = 0; n < array->size(); n++) {
+        const cppbor::Int* itemInt = ((*array)[n])->asInt();
+        if (itemInt == nullptr) {
+            LOG(ERROR) << "An item in the accessControlProfileIds array is not a bstr";
+            return {};
+        }
+        accessControlProfileIds.push_back(itemInt->value());
+    }
+    return accessControlProfileIds;
+}
+
+optional<vector<vector<uint8_t>>> parseEncryptedChunks(const cppbor::Item& item) {
+    const cppbor::Array* array = item.asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "The encryptedChunks member is not an array";
+        return {};
+    }
+
+    vector<vector<uint8_t>> encryptedChunks;
+    for (size_t n = 0; n < array->size(); n++) {
+        const cppbor::Bstr* itemBstr = ((*array)[n])->asBstr();
+        if (itemBstr == nullptr) {
+            LOG(ERROR) << "An item in the encryptedChunks array is not a bstr";
+            return {};
+        }
+        encryptedChunks.push_back(itemBstr->value());
+    }
+    return encryptedChunks;
+}
+
+bool CredentialData::loadFromDisk() {
+
+    // Reset all data.
+    credentialData_.clear();
+    attestationCertificate_.clear();
+    secureAccessControlProfiles_.clear();
+    idToEncryptedChunks_.clear();
+    authKeyDatas_.clear();
+    keyCount_ = 0;
+    maxUsesPerKey_ = 1;
+
+    optional<vector<uint8_t>> data = fileGetContents(fileName_);
+    if (!data) {
+        LOG(ERROR) << "Error loading data";
+        return false;
+    }
+
+    auto [item, _ /* newPos */, message] = cppbor::parse(data.value());
+    if (item == nullptr) {
+        LOG(ERROR) << "Data loaded from " << fileName_ << " is not valid CBOR: " << message;
+        return false;
+    }
+
+    const cppbor::Map* map = item->asMap();
+    if (map == nullptr) {
+        LOG(ERROR) << "Top-level item is not a map";
+        return false;
+    }
+
+    for (size_t n = 0; n < map->size(); n++) {
+        auto [keyItem, valueItem] = (*map)[n];
+        const cppbor::Tstr* tstr = keyItem->asTstr();
+        if (tstr == nullptr) {
+            LOG(ERROR) << "Key item in top-level map is not a tstr";
+            return false;
+        }
+        const string& key = tstr->value();
+
+        if (key == "secureUserId") {
+            const cppbor::Int* number = valueItem->asInt();
+            if (number == nullptr) {
+                LOG(ERROR) << "Value for secureUserId is not a number";
+                return false;
+            }
+            secureUserId_ = number->value();
+        } else if (key == "credentialData") {
+            const cppbor::Bstr* valueBstr = valueItem->asBstr();
+            if (valueBstr == nullptr) {
+                LOG(ERROR) << "Value for credentialData is not a bstr";
+                return false;
+            }
+            credentialData_ = valueBstr->value();
+        } else if (key == "attestationCertificate") {
+            const cppbor::Bstr* valueBstr = valueItem->asBstr();
+            if (valueBstr == nullptr) {
+                LOG(ERROR) << "Value for attestationCertificate is not a bstr";
+                return false;
+            }
+            attestationCertificate_ = valueBstr->value();
+        } else if (key == "secureAccessControlProfiles") {
+            const cppbor::Array* array = valueItem->asArray();
+            if (array == nullptr) {
+                LOG(ERROR) << "Value for attestationCertificate is not an array";
+                return false;
+            }
+            for (size_t m = 0; m < array->size(); m++) {
+                const std::unique_ptr<cppbor::Item>& item = (*array)[m];
+                optional<SecureAccessControlProfile> sacp = parseSacp(*item);
+                if (!sacp) {
+                    LOG(ERROR) << "Error parsing SecureAccessControlProfile";
+                    return false;
+                }
+                secureAccessControlProfiles_.push_back(sacp.value());
+            }
+
+        } else if (key == "entryData") {
+            const cppbor::Map* map = valueItem->asMap();
+            if (map == nullptr) {
+                LOG(ERROR) << "Value for encryptedChunks is not an map";
+                return false;
+            }
+            for (size_t m = 0; m < map->size(); m++) {
+                auto [ecKeyItem, ecValueItem] = (*map)[m];
+                const cppbor::Tstr* ecTstr = ecKeyItem->asTstr();
+                if (ecTstr == nullptr) {
+                    LOG(ERROR) << "Key item in encryptedChunks map is not a tstr";
+                    return false;
+                }
+                const string& ecId = ecTstr->value();
+
+                const cppbor::Array* ecEntryArrayItem = ecValueItem->asArray();
+                if (ecEntryArrayItem == nullptr || ecEntryArrayItem->size() < 3) {
+                    LOG(ERROR) << "Value item in encryptedChunks map is an array with at least two "
+                                  "elements";
+                    return false;
+                }
+                const cppbor::Int* ecEntrySizeItem = (*ecEntryArrayItem)[0]->asInt();
+                if (ecEntrySizeItem == nullptr) {
+                    LOG(ERROR) << "Entry size not a number";
+                    return false;
+                }
+                uint64_t entrySize = ecEntrySizeItem->value();
+
+                optional<vector<uint16_t>> accessControlProfileIds =
+                    parseAccessControlProfileIds(*(*ecEntryArrayItem)[1]);
+                if (!accessControlProfileIds) {
+                    LOG(ERROR) << "Error parsing access control profile ids";
+                    return false;
+                }
+
+                optional<vector<vector<uint8_t>>> encryptedChunks =
+                    parseEncryptedChunks(*(*ecEntryArrayItem)[2]);
+                if (!encryptedChunks) {
+                    LOG(ERROR) << "Error parsing encrypted chunks";
+                    return false;
+                }
+
+                EntryData data;
+                data.size = entrySize;
+                data.accessControlProfileIds = accessControlProfileIds.value();
+                data.encryptedChunks = encryptedChunks.value();
+                idToEncryptedChunks_[ecId] = data;
+            }
+
+        } else if (key == "authKeyData") {
+            const cppbor::Array* array = valueItem->asArray();
+            if (array == nullptr) {
+                LOG(ERROR) << "Value for authData is not an array";
+                return false;
+            }
+            for (size_t m = 0; m < array->size(); m++) {
+                const std::unique_ptr<cppbor::Item>& item = (*array)[m];
+                optional<AuthKeyData> authKeyData = parseAuthKeyData(*item);
+                if (!authKeyData) {
+                    LOG(ERROR) << "Error parsing AuthKeyData";
+                    return false;
+                }
+                authKeyDatas_.push_back(authKeyData.value());
+            }
+
+        } else if (key == "authKeyCount") {
+            const cppbor::Int* number = valueItem->asInt();
+            if (number == nullptr) {
+                LOG(ERROR) << "Value for authKeyCount is not a number";
+                return false;
+            }
+            keyCount_ = number->value();
+
+        } else if (key == "maxUsesPerAuthKey") {
+            const cppbor::Int* number = valueItem->asInt();
+            if (number == nullptr) {
+                LOG(ERROR) << "Value for maxUsesPerAuthKey is not a number";
+                return false;
+            }
+            maxUsesPerKey_ = number->value();
+        }
+    }
+
+    if (credentialData_.size() == 0 || attestationCertificate_.size() == 0) {
+        LOG(ERROR) << "Missing credentialData or attestationCertificate";
+        return false;
+    }
+
+    if (size_t(keyCount_) != authKeyDatas_.size()) {
+        LOG(ERROR) << "keyCount_=" << keyCount_
+                   << " != authKeyDatas_.size()=" << authKeyDatas_.size();
+        return false;
+    }
+
+    return true;
+}
+
+const vector<uint8_t>& CredentialData::getCredentialData() const {
+    return credentialData_;
+}
+
+int64_t CredentialData::getSecureUserId() {
+    return secureUserId_;
+}
+
+const vector<uint8_t>& CredentialData::getAttestationCertificate() const {
+    return attestationCertificate_;
+}
+
+const vector<SecureAccessControlProfile>& CredentialData::getSecureAccessControlProfiles() const {
+    return secureAccessControlProfiles_;
+}
+
+bool CredentialData::hasEntryData(const string& namespaceName, const string& entryName) const {
+    string id = namespaceName + ":" + entryName;
+    auto iter = idToEncryptedChunks_.find(id);
+    if (iter == idToEncryptedChunks_.end()) {
+        return false;
+    }
+    return true;
+}
+
+optional<EntryData> CredentialData::getEntryData(const string& namespaceName,
+                                                 const string& entryName) const {
+    string id = namespaceName + ":" + entryName;
+    auto iter = idToEncryptedChunks_.find(id);
+    if (iter == idToEncryptedChunks_.end()) {
+        return {};
+    }
+    return iter->second;
+}
+
+bool CredentialData::deleteCredential() {
+    if (unlink(fileName_.c_str()) != 0) {
+        PLOG(ERROR) << "Error deleting " << fileName_;
+        return false;
+    }
+    return true;
+}
+
+optional<bool> CredentialData::credentialExists(const string& dataPath, uid_t ownerUid,
+                                                const string& name) {
+    struct stat statbuf;
+    string filename = calculateCredentialFileName(dataPath, ownerUid, name);
+    if (stat(filename.c_str(), &statbuf) != 0) {
+        if (errno == ENOENT) {
+            return false;
+        }
+        PLOG(ERROR) << "Error getting information about " << filename;
+        return {};
+    }
+    return true;
+}
+
+// ---
+
+void CredentialData::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+    keyCount_ = keyCount;
+    maxUsesPerKey_ = maxUsesPerKey;
+
+    // If growing the number of auth keys (prevKeyCount < keyCount_ case) we'll add
+    // new AuthKeyData structs to |authKeyDatas_| and each struct will have empty |certificate|
+    // and |pendingCertificate| fields. Those will be filled out when the
+    // getAuthKeysNeedingCertification() is called.
+    //
+    // If shrinking, we'll just delete the AuthKeyData structs at the end. There's nothing
+    // else to do, the HAL doesn't need to know we're nuking these authentication keys.
+    //
+    // Therefore, in either case it's as simple as just resizing the vector.
+    authKeyDatas_.resize(keyCount_);
+}
+
+const vector<AuthKeyData>& CredentialData::getAuthKeyDatas() const {
+    return authKeyDatas_;
+}
+
+const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys) {
+    AuthKeyData* candidate = nullptr;
+
+    int n = 0;
+    int candidateNum = -1;
+    for (AuthKeyData& data : authKeyDatas_) {
+        if (data.certificate.size() != 0) {
+            if (candidate == nullptr || data.useCount < candidate->useCount) {
+                candidate = &data;
+                candidateNum = n;
+            }
+        }
+        n++;
+    }
+
+    if (candidate == nullptr) {
+        return nullptr;
+    }
+
+    if (candidate->useCount >= maxUsesPerKey_ && !allowUsingExhaustedKeys) {
+        return nullptr;
+    }
+
+    candidate->useCount += 1;
+    return candidate;
+}
+
+optional<vector<vector<uint8_t>>> CredentialData::getAuthKeysNeedingCertification(
+    const sp<android::hardware::identity::V1_0::IIdentityCredential>& halBinder) {
+
+    vector<vector<uint8_t>> keysNeedingCert;
+
+    for (AuthKeyData& data : authKeyDatas_) {
+        bool newKeyNeeded = (data.certificate.size() == 0) || (data.useCount >= maxUsesPerKey_);
+        bool certificationPending = (data.pendingCertificate.size() > 0);
+        if (newKeyNeeded && !certificationPending) {
+            Result result;
+            vector<uint8_t> signingKeyBlob;
+            vector<uint8_t> signingKeyCertificate;
+            halBinder->generateSigningKeyPair(
+                [&](const Result& _result,
+                    const android::hardware::hidl_vec<uint8_t> _signingKeyBlob,
+                    const android::hardware::hidl_vec<uint8_t> _signingKeyCertificate) {
+                    result = _result;
+                    signingKeyBlob = _signingKeyBlob;
+                    signingKeyCertificate = _signingKeyCertificate;
+                });
+            if (result.code != ResultCode::OK) {
+                LOG(ERROR) << "Error generating signing key-pair";
+                return {};
+            }
+            data.pendingCertificate = signingKeyCertificate;
+            data.pendingKeyBlob = signingKeyBlob;
+            certificationPending = true;
+        }
+
+        if (certificationPending) {
+            keysNeedingCert.push_back(data.pendingCertificate);
+        }
+    }
+    return keysNeedingCert;
+}
+
+bool CredentialData::storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+                                                   const vector<uint8_t>& staticAuthData) {
+    for (AuthKeyData& data : authKeyDatas_) {
+        if (data.pendingCertificate == authenticationKey) {
+            data.certificate = data.pendingCertificate;
+            data.keyBlob = data.pendingKeyBlob;
+            data.staticAuthenticationData = staticAuthData;
+            data.pendingCertificate.clear();
+            data.pendingKeyBlob.clear();
+            data.useCount = 0;
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
new file mode 100644
index 0000000..39e41f8
--- /dev/null
+++ b/identity/CredentialData.h
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_CREDENTIAL_DATA_H_
+#define SYSTEM_SECURITY_CREDENTIAL_DATA_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android/hardware/identity/1.0/IIdentityCredential.h>
+#include <android/hardware/identity/1.0/types.h>
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+using ::std::map;
+using ::std::optional;
+using ::std::pair;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+struct EntryData {
+    EntryData() {}
+
+    uint64_t size = 0;
+    vector<uint16_t> accessControlProfileIds;
+    vector<vector<uint8_t>> encryptedChunks;
+};
+
+struct AuthKeyData {
+    AuthKeyData() {}
+
+    vector<uint8_t> certificate;
+    vector<uint8_t> keyBlob;
+    vector<uint8_t> staticAuthenticationData;
+    vector<uint8_t> pendingCertificate;
+    vector<uint8_t> pendingKeyBlob;
+    int useCount = 0;
+};
+
+class CredentialData : public RefBase {
+  public:
+    CredentialData(const string& dataPath, uid_t ownerUid, const string& name);
+
+    static string calculateCredentialFileName(const string& dataPath, uid_t ownerUid,
+                                              const string& name);
+
+    static optional<bool> credentialExists(const string& dataPath, uid_t ownerUid,
+                                           const string& name);
+
+    void setSecureUserId(int64_t secureUserId);
+
+    void setCredentialData(const vector<uint8_t>& credentialData);
+
+    void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
+
+    void
+    addSecureAccessControlProfile(const SecureAccessControlProfile& secureAccessControlProfile);
+
+    void addEntryData(const string& namespaceName, const string& entryName, const EntryData& data);
+
+    bool saveToDisk() const;
+
+    bool loadFromDisk();
+
+    bool deleteCredential();
+
+    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+
+    // Getters
+
+    int64_t getSecureUserId();
+
+    const vector<uint8_t>& getCredentialData() const;
+
+    const vector<uint8_t>& getAttestationCertificate() const;
+
+    const vector<SecureAccessControlProfile>& getSecureAccessControlProfiles() const;
+
+    bool hasEntryData(const string& namespaceName, const string& entryName) const;
+
+    optional<EntryData> getEntryData(const string& namespaceName, const string& entryName) const;
+
+    const vector<AuthKeyData>& getAuthKeyDatas() const;
+
+    // Returns |nullptr| if a suitable key cannot be found. Otherwise returns
+    // the authentication and increases its use-count.
+    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys);
+
+    optional<vector<vector<uint8_t>>> getAuthKeysNeedingCertification(
+        const sp<android::hardware::identity::V1_0::IIdentityCredential>& halBinder);
+
+    bool storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+                                       const vector<uint8_t>& staticAuthData);
+
+  private:
+    // Set by constructor.
+    //
+    string dataPath_;
+    uid_t ownerUid_;
+    string name_;
+
+    // Calculated at construction time, from |dataPath_|, |ownerUid_|, |name_|.
+    string fileName_;
+
+    // Data serialized in CBOR from here:
+    //
+    int64_t secureUserId_;
+    vector<uint8_t> credentialData_;
+    vector<uint8_t> attestationCertificate_;
+    vector<SecureAccessControlProfile> secureAccessControlProfiles_;
+    map<string, EntryData> idToEncryptedChunks_;
+
+    int keyCount_ = 0;
+    int maxUsesPerKey_ = 1;
+    vector<AuthKeyData> authKeyDatas_;  // Always |keyCount_| long.
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_CREDENTIAL_DATA_H_
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
new file mode 100644
index 0000000..b13a7b0
--- /dev/null
+++ b/identity/CredentialStore.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CredentialStore"
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+
+#include <binder/IPCThreadState.h>
+
+#include "Credential.h"
+#include "CredentialStore.h"
+#include "Util.h"
+#include "WritableCredential.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+
+using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
+
+CredentialStore::CredentialStore(const std::string& dataPath, sp<IIdentityCredentialStore> hal)
+    : dataPath_(dataPath), hal_(hal) {}
+
+bool CredentialStore::init() {
+    Result result;
+    hal_->getHardwareInformation([&](const Result& _result, const hidl_string& credentialStoreName,
+                                     const hidl_string& credentialStoreAuthorName,
+                                     uint32_t _dataChunkSize, bool _isDirectAccess,
+                                     const hidl_vec<hidl_string>& _supportedDocTypes) {
+        result = _result;
+        dataChunkSize_ = _dataChunkSize;
+        isDirectAccess_ = _isDirectAccess;
+        supportedDocTypes_.clear();
+        for (auto& docType : _supportedDocTypes) {
+            supportedDocTypes_.push_back(docType);
+        }
+        LOG(INFO) << "Connected to Identity Credential HAL with name '" << credentialStoreName
+                  << "' authored by '" << credentialStoreAuthorName << "' with chunk size "
+                  << _dataChunkSize << " and directoAccess set to "
+                  << (_isDirectAccess ? "true" : "false");
+    });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "Error getting hardware information: " << (int)result.code << ": "
+                   << result.message;
+        return false;
+    }
+    return true;
+}
+
+CredentialStore::~CredentialStore() {}
+
+Status CredentialStore::getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) {
+    SecurityHardwareInfoParcel info;
+    info.directAccess = isDirectAccess_;
+    info.supportedDocTypes = supportedDocTypes_;
+    *_aidl_return = info;
+    return Status::ok();
+};
+
+Status CredentialStore::createCredential(const std::string& credentialName,
+                                         const std::string& docType,
+                                         sp<IWritableCredential>* _aidl_return) {
+    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+    optional<bool> credentialExists =
+        CredentialData::credentialExists(dataPath_, callingUid, credentialName);
+    if (!credentialExists.has_value()) {
+        return Status::fromServiceSpecificError(
+            ERROR_GENERIC, "Error determining if credential with given name exists");
+    }
+    if (credentialExists.value()) {
+        return Status::fromServiceSpecificError(ERROR_ALREADY_PERSONALIZED,
+                                                "Credential with given name already exists");
+    }
+
+    if (supportedDocTypes_.size() > 0) {
+        if (std::find(supportedDocTypes_.begin(), supportedDocTypes_.end(), docType) ==
+            supportedDocTypes_.end()) {
+            return Status::fromServiceSpecificError(ERROR_DOCUMENT_TYPE_NOT_SUPPORTED,
+                                                    "No support for given document type");
+        }
+    }
+
+    Result result;
+    sp<IWritableIdentityCredential> halWritableCredential;
+    hal_->createCredential(
+        docType, false,
+        [&](const Result& _result, const sp<IWritableIdentityCredential>& _halWritableCredential) {
+            result = _result;
+            halWritableCredential = _halWritableCredential;
+        });
+    if (result.code != ResultCode::OK) {
+        return halResultToGenericError(result);
+    }
+
+    sp<IWritableCredential> writableCredential = new WritableCredential(
+        dataPath_, credentialName, docType, dataChunkSize_, halWritableCredential);
+    *_aidl_return = writableCredential;
+    return Status::ok();
+}
+
+// Keep in sync with IdentityCredentialStore.java
+//
+
+const int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1;
+
+Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+                                            sp<ICredential>* _aidl_return) {
+    *_aidl_return = nullptr;
+
+    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+    optional<bool> credentialExists =
+        CredentialData::credentialExists(dataPath_, callingUid, credentialName);
+    if (!credentialExists.has_value()) {
+        return Status::fromServiceSpecificError(
+            ERROR_GENERIC, "Error determining if credential with given name exists");
+    }
+    if (!credentialExists.value()) {
+        return Status::fromServiceSpecificError(ERROR_NO_SUCH_CREDENTIAL,
+                                                "Credential with given name doesn't exist");
+    }
+
+    // We only support a single cipher-suite right now.
+    if (cipherSuite != CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+        return Status::fromServiceSpecificError(ERROR_CIPHER_SUITE_NOT_SUPPORTED,
+                                                "Cipher suite not supported");
+    }
+
+    sp<Credential> credential = new Credential(dataPath_, credentialName);
+
+    Status loadStatus = credential->loadCredential(hal_);
+    if (!loadStatus.isOk()) {
+        LOG(ERROR) << "Error loading credential";
+    } else {
+        *_aidl_return = credential;
+    }
+    return loadStatus;
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
new file mode 100644
index 0000000..8d679a2
--- /dev/null
+++ b/identity/CredentialStore.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_CREDENTIAL_STORE_H_
+#define SYSTEM_SECURITY_CREDENTIAL_STORE_H_
+
+#include <string>
+#include <vector>
+
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+#include <android/hardware/identity/1.0/types.h>
+
+#include <android/security/identity/BnCredentialStore.h>
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+using ::std::unique_ptr;
+using ::std::vector;
+
+using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
+
+class CredentialStore : public BnCredentialStore {
+  public:
+    CredentialStore(const string& dataPath, sp<IIdentityCredentialStore> hal);
+    ~CredentialStore();
+
+    bool init();
+
+    // ICredentialStore overrides
+    Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
+
+    Status createCredential(const string& credentialName, const string& docType,
+                            sp<IWritableCredential>* _aidl_return) override;
+
+    Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
+                               sp<ICredential>* _aidl_return) override;
+
+  private:
+    string dataPath_;
+
+    sp<IIdentityCredentialStore> hal_;
+
+    bool isDirectAccess_;
+    vector<string> supportedDocTypes_;
+    size_t dataChunkSize_;
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_CREDENTIAL_STORE_H_
diff --git a/identity/CredentialStoreFactory.cpp b/identity/CredentialStoreFactory.cpp
new file mode 100644
index 0000000..947adf3
--- /dev/null
+++ b/identity/CredentialStoreFactory.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CredentialStoreFactory"
+
+#include <android-base/logging.h>
+
+#include <binder/IPCThreadState.h>
+
+#include "CredentialStore.h"
+#include "CredentialStoreFactory.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+
+CredentialStoreFactory::CredentialStoreFactory(const std::string& dataPath) : dataPath_(dataPath) {}
+
+CredentialStoreFactory::~CredentialStoreFactory() {}
+
+CredentialStore* CredentialStoreFactory::createCredentialStore(const string& serviceName) {
+    sp<IIdentityCredentialStore> hal = IIdentityCredentialStore::tryGetService(serviceName);
+    if (hal.get() == nullptr) {
+        LOG(ERROR) << "Error get hal for store with service name '" << serviceName << "'";
+        return nullptr;
+    }
+
+    CredentialStore* store = new CredentialStore(dataPath_, hal);
+    if (!store->init()) {
+        LOG(ERROR) << "Error initializing CredentialStore with service name '" << serviceName
+                   << "'";
+        delete store;
+        return nullptr;
+    }
+
+    return store;
+}
+
+Status CredentialStoreFactory::getCredentialStore(int32_t credentialStoreType,
+                                                  sp<ICredentialStore>* _aidl_return) {
+    switch (credentialStoreType) {
+    case CREDENTIAL_STORE_TYPE_DEFAULT:
+        if (defaultStore_.get() == nullptr) {
+            defaultStore_ = createCredentialStore("default");
+        }
+        if (defaultStore_.get() == nullptr) {
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error creating default store");
+        }
+        *_aidl_return = defaultStore_.get();
+        return Status::ok();
+
+    case CREDENTIAL_STORE_TYPE_DIRECT_ACCESS:
+        if (directAccessStore_.get() == nullptr) {
+            directAccessStore_ = createCredentialStore("directAccess");
+        }
+        if (directAccessStore_.get() == nullptr) {
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error creating direct access store");
+        }
+        *_aidl_return = directAccessStore_.get();
+        return Status::ok();
+        break;
+    }
+
+    return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                            "Unknown credential store type");
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/CredentialStoreFactory.h b/identity/CredentialStoreFactory.h
new file mode 100644
index 0000000..3aef618
--- /dev/null
+++ b/identity/CredentialStoreFactory.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
+#define SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
+
+#include <android/security/identity/BnCredentialStoreFactory.h>
+
+#include "CredentialStore.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+
+class CredentialStoreFactory : public BnCredentialStoreFactory {
+  public:
+    explicit CredentialStoreFactory(const string& dataPath);
+    ~CredentialStoreFactory();
+
+    Status getCredentialStore(int32_t credentialStoreType,
+                              sp<ICredentialStore>* _aidl_return) override;
+
+  private:
+    CredentialStore* createCredentialStore(const string& serviceName);
+
+    string dataPath_;
+
+    sp<CredentialStore> defaultStore_;
+    sp<CredentialStore> directAccessStore_;
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
diff --git a/identity/Util.cpp b/identity/Util.cpp
new file mode 100644
index 0000000..c397913
--- /dev/null
+++ b/identity/Util.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Util"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <android/security/identity/ICredentialStore.h>
+
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::base::StringPrintf;
+
+Status halResultToGenericError(const Result& result) {
+    string message =
+        StringPrintf("HAL failed with code %d: %s", int(result.code), result.message.c_str());
+    return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, message.c_str());
+}
+
+optional<vector<uint8_t>> fileGetContents(const string& path) {
+    int fd = open(path.c_str(), O_RDONLY);
+    if (fd == -1) {
+        PLOG(ERROR) << "Error opening " << path;
+        return {};
+    }
+
+    struct stat statbuf;
+    if (fstat(fd, &statbuf) != 0) {
+        PLOG(ERROR) << "Error statting " << path;
+        close(fd);
+        return {};
+    }
+    vector<uint8_t> data;
+    data.resize(statbuf.st_size);
+
+    uint8_t* p = data.data();
+    size_t remaining = data.size();
+    while (remaining > 0) {
+        size_t numRead = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+        if (numRead <= 0) {
+            PLOG(ERROR) << "Failed reading from '" << path << "'";
+            close(fd);
+            return {};
+        }
+        p += numRead;
+        remaining -= numRead;
+    }
+    close(fd);
+
+    return data;
+}
+
+bool fileSetContents(const string& path, const vector<uint8_t>& data) {
+    char tempName[4096];
+    int fd;
+
+    string tempNameStr = path + ".XXXXXX";
+    if (tempNameStr.size() >= sizeof tempName - 1) {
+        LOG(ERROR) << "Path name too long";
+        return false;
+    }
+    strncpy(tempName, tempNameStr.c_str(), sizeof tempName);
+
+    fd = mkstemp(tempName);
+    if (fd == -1) {
+        PLOG(ERROR) << "Error creating temp file for '" << path << "'";
+        return false;
+    }
+
+    const uint8_t* p = data.data();
+    size_t remaining = data.size();
+    while (remaining > 0) {
+        size_t numWritten = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+        if (numWritten <= 0) {
+            PLOG(ERROR) << "Failed writing into temp file for '" << path << "'";
+            close(fd);
+            return false;
+        }
+        p += numWritten;
+        remaining -= numWritten;
+    }
+
+    if (TEMP_FAILURE_RETRY(fsync(fd) == -1)) {
+        PLOG(ERROR) << "Failed fsyncing temp file for '" << path << "'";
+        close(fd);
+        return false;
+    }
+    close(fd);
+
+    if (rename(tempName, path.c_str()) != 0) {
+        PLOG(ERROR) << "Error renaming temp file for '" << path << "'";
+        close(fd);
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/Util.h b/identity/Util.h
new file mode 100644
index 0000000..bb0e275
--- /dev/null
+++ b/identity/Util.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_IDENTITY_UTIL_H_
+#define SYSTEM_SECURITY_IDENTITY_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+#include <android/hardware/identity/1.0/types.h>
+#include <binder/Status.h>
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+using ::android::binder::Status;
+using ::android::hardware::identity::V1_0::Result;
+
+Status halResultToGenericError(const Result& result);
+
+// Helper function to atomically write |data| into file at |path|.
+//
+// Returns true on success, false on error.
+//
+bool fileSetContents(const string& path, const vector<uint8_t>& data);
+
+// Helper function which reads contents offile at |path| into |data|.
+//
+// Returns nothing on error, the content on success.
+//
+optional<vector<uint8_t>> fileGetContents(const string& path);
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_IDENTITY_UTIL_H_
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
new file mode 100644
index 0000000..f58ec16
--- /dev/null
+++ b/identity/WritableCredential.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WritableCredential"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android/security/identity/ICredentialStore.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include "CredentialData.h"
+#include "Util.h"
+#include "WritableCredential.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::std::pair;
+
+using ::android::hardware::hidl_vec;
+
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+
+using ::android::hardware::identity::support::chunkVector;
+
+WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
+                                       const string& /*docType*/, size_t dataChunkSize,
+                                       sp<IWritableIdentityCredential> halBinder)
+    : dataPath_(dataPath), credentialName_(credentialName), dataChunkSize_(dataChunkSize),
+      halBinder_(halBinder) {}
+
+WritableCredential::~WritableCredential() {}
+
+Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
+    vector<uint8_t> attestationCertificate;
+
+    if (!attestationCertificate_.empty()) {
+        return Status::ok();
+    }
+
+    Result result;
+    halBinder_->getAttestationCertificate(
+        challenge, [&](const Result& _result, const hidl_vec<uint8_t>& _attestationCertificate) {
+            result = _result;
+            attestationCertificate = _attestationCertificate;
+        });
+    if (result.code != ResultCode::OK) {
+        LOG(ERROR) << "Error calling getAttestationCertificate()";
+        return halResultToGenericError(result);
+    }
+    attestationCertificate_ = attestationCertificate;
+    return Status::ok();
+}
+
+Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
+                                                            vector<uint8_t>* _aidl_return) {
+
+    Status ensureStatus = ensureAttestationCertificateExists(challenge);
+    if (!ensureStatus.isOk()) {
+        return ensureStatus;
+    }
+
+    *_aidl_return = attestationCertificate_;
+    return Status::ok();
+}
+
+Status
+WritableCredential::personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
+                                const vector<EntryNamespaceParcel>& entryNamespaces,
+                                int64_t secureUserId, vector<uint8_t>* _aidl_return) {
+    Status ensureStatus = ensureAttestationCertificateExists({});
+    if (!ensureStatus.isOk()) {
+        return ensureStatus;
+    }
+
+    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+    CredentialData data = CredentialData(dataPath_, callingUid, credentialName_);
+
+    // Note: The value 0 is used to convey that no user-authentication is needed for this
+    // credential. This is to allow creating credentials w/o user authentication on devices
+    // where Secure lock screen is not enabled.
+    data.setSecureUserId(secureUserId);
+
+    data.setAttestationCertificate(attestationCertificate_);
+
+    vector<uint16_t> entryCounts;
+    for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
+        entryCounts.push_back(ensParcel.entries.size());
+    }
+
+    Result result;
+    halBinder_->startPersonalization(accessControlProfiles.size(), entryCounts,
+                                     [&](const Result& _result) { result = _result; });
+    if (result.code != ResultCode::OK) {
+        return halResultToGenericError(result);
+    }
+
+    for (const AccessControlProfileParcel& acpParcel : accessControlProfiles) {
+        halBinder_->addAccessControlProfile(
+            acpParcel.id, acpParcel.readerCertificate, acpParcel.userAuthenticationRequired,
+            acpParcel.userAuthenticationTimeoutMillis, secureUserId,
+            [&](const Result& _result, const SecureAccessControlProfile& profile) {
+                data.addSecureAccessControlProfile(profile);
+                result = _result;
+            });
+        if (result.code != ResultCode::OK) {
+            return halResultToGenericError(result);
+        }
+    }
+
+    for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
+        for (const EntryParcel& eParcel : ensParcel.entries) {
+            vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, dataChunkSize_);
+
+            vector<uint16_t> ids;
+            std::copy(eParcel.accessControlProfileIds.begin(),
+                      eParcel.accessControlProfileIds.end(), std::back_inserter(ids));
+
+            halBinder_->beginAddEntry(ids, ensParcel.namespaceName, eParcel.name,
+                                      eParcel.value.size(),
+                                      [&](const Result& _result) { result = _result; });
+            if (result.code != ResultCode::OK) {
+                return halResultToGenericError(result);
+            }
+
+            vector<vector<uint8_t>> encryptedChunks;
+            for (const auto& chunk : chunks) {
+                halBinder_->addEntryValue(
+                    chunk, [&](const Result& _result, const hidl_vec<uint8_t>& encryptedContent) {
+                        result = _result;
+                        encryptedChunks.push_back(encryptedContent);
+                    });
+                if (result.code != ResultCode::OK) {
+                    return halResultToGenericError(result);
+                }
+            }
+            EntryData eData;
+            eData.size = eParcel.value.size();
+            eData.accessControlProfileIds = std::move(ids);
+            eData.encryptedChunks = std::move(encryptedChunks);
+            data.addEntryData(ensParcel.namespaceName, eParcel.name, eData);
+        }
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    halBinder_->finishAddingEntries([&](const Result& _result,
+                                        const hidl_vec<uint8_t>& _credentialData,
+                                        const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
+        data.setCredentialData(_credentialData);
+        result = _result;
+        credentialData = _credentialData;
+        proofOfProvisioningSignature = _proofOfProvisioningSignature;
+    });
+    if (result.code != ResultCode::OK) {
+        return halResultToGenericError(result);
+    }
+
+    if (!data.saveToDisk()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error saving credential data to disk");
+    }
+
+    *_aidl_return = proofOfProvisioningSignature;
+    return Status::ok();
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
new file mode 100644
index 0000000..47987ce
--- /dev/null
+++ b/identity/WritableCredential.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
+#define SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
+
+#include <string>
+#include <vector>
+
+#include <android/security/identity/BnWritableCredential.h>
+
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+#include <android/hardware/identity/1.0/types.h>
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::binder::Status;
+using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
+using ::std::string;
+using ::std::vector;
+
+class WritableCredential : public BnWritableCredential {
+  public:
+    WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
+                       size_t dataChunkSize, sp<IWritableIdentityCredential> halBinder);
+    ~WritableCredential();
+
+    // IWritableCredential overrides
+    Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
+                                            vector<uint8_t>* _aidl_return) override;
+
+    Status personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
+                       const vector<EntryNamespaceParcel>& entryNamespaces, int64_t secureUserId,
+                       vector<uint8_t>* _aidl_return) override;
+
+  private:
+    string dataPath_;
+    string credentialName_;
+    size_t dataChunkSize_;
+    sp<IWritableIdentityCredential> halBinder_;
+    vector<uint8_t> attestationCertificate_;
+
+    Status ensureAttestationCertificateExists(const vector<uint8_t>& challenge);
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
diff --git a/identity/binder/android/security/identity/AccessControlProfileParcel.aidl b/identity/binder/android/security/identity/AccessControlProfileParcel.aidl
new file mode 100644
index 0000000..4bb85a9
--- /dev/null
+++ b/identity/binder/android/security/identity/AccessControlProfileParcel.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable AccessControlProfileParcel {
+    int id;
+    byte[] readerCertificate;
+    boolean userAuthenticationRequired;
+    long userAuthenticationTimeoutMillis;
+}
diff --git a/identity/binder/android/security/identity/AuthKeyParcel.aidl b/identity/binder/android/security/identity/AuthKeyParcel.aidl
new file mode 100644
index 0000000..9374b8e
--- /dev/null
+++ b/identity/binder/android/security/identity/AuthKeyParcel.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable AuthKeyParcel {
+    byte[] x509cert;
+}
diff --git a/identity/binder/android/security/identity/EntryNamespaceParcel.aidl b/identity/binder/android/security/identity/EntryNamespaceParcel.aidl
new file mode 100644
index 0000000..50758c8
--- /dev/null
+++ b/identity/binder/android/security/identity/EntryNamespaceParcel.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.EntryParcel;
+
+/**
+ * @hide
+ */
+parcelable EntryNamespaceParcel {
+    @utf8InCpp String namespaceName;
+    EntryParcel[] entries;
+}
diff --git a/identity/binder/android/security/identity/EntryParcel.aidl b/identity/binder/android/security/identity/EntryParcel.aidl
new file mode 100644
index 0000000..8d236d4
--- /dev/null
+++ b/identity/binder/android/security/identity/EntryParcel.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable EntryParcel {
+    @utf8InCpp String name;
+    byte[] value;
+    int[] accessControlProfileIds;
+}
diff --git a/identity/binder/android/security/identity/GetEntriesResultParcel.aidl b/identity/binder/android/security/identity/GetEntriesResultParcel.aidl
new file mode 100644
index 0000000..03b363c
--- /dev/null
+++ b/identity/binder/android/security/identity/GetEntriesResultParcel.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.ResultNamespaceParcel;
+
+/**
+ * @hide
+ */
+parcelable GetEntriesResultParcel {
+    ResultNamespaceParcel[] resultNamespaces;
+    byte[] deviceNameSpaces;
+    byte[] mac;
+    byte[] staticAuthenticationData;
+}
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
new file mode 100644
index 0000000..7bd0df7
--- /dev/null
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.RequestNamespaceParcel;
+import android.security.identity.GetEntriesResultParcel;
+import android.security.identity.AuthKeyParcel;
+
+/**
+ * @hide
+ */
+interface ICredential {
+    /* The STATUS_* constants are used in the status field in ResultEntryParcel.
+     * Keep in sync with ResultNamespace.java.
+     */
+    const int STATUS_OK = 0;
+    const int STATUS_NO_SUCH_ENTRY = 1;
+    const int STATUS_NOT_REQUESTED = 2;
+    const int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+    const int STATUS_USER_AUTHENTICATION_FAILED = 4;
+    const int STATUS_READER_AUTHENTICATION_FAILED = 5;
+    const int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+    byte[] createEphemeralKeyPair();
+
+    void setReaderEphemeralPublicKey(in byte[] publicKey);
+
+    byte[] deleteCredential();
+
+    byte[] getCredentialKeyCertificateChain();
+
+    long selectAuthKey(in boolean allowUsingExhaustedKeys);
+
+    GetEntriesResultParcel getEntries(in byte[] requestMessage,
+                                      in RequestNamespaceParcel[] requestNamespaces,
+                                      in byte[] sessionTranscript,
+                                      in byte[] readerSignature,
+                                      in boolean allowUsingExhaustedKeys);
+
+    void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
+
+    AuthKeyParcel[] getAuthKeysNeedingCertification();
+
+    void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey, in byte[] staticAuthData);
+
+    int[] getAuthenticationDataUsageCount();
+}
+
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
new file mode 100644
index 0000000..1039831
--- /dev/null
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.IWritableCredential;
+import android.security.identity.ICredential;
+import android.security.identity.SecurityHardwareInfoParcel;
+
+/**
+ * @hide
+ */
+interface ICredentialStore {
+    /* All binder calls may return a ServiceSpecificException
+     * with the following error codes:
+     */
+    const int ERROR_NONE = 0;
+    const int ERROR_GENERIC = 1;
+    const int ERROR_ALREADY_PERSONALIZED = 2;
+    const int ERROR_NO_SUCH_CREDENTIAL = 3;
+    const int ERROR_CIPHER_SUITE_NOT_SUPPORTED = 4;
+    const int ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 5;
+    const int ERROR_NO_AUTHENTICATION_KEY_AVAILABLE = 6;
+    const int ERROR_INVALID_READER_SIGNATURE = 7;
+    const int ERROR_DOCUMENT_TYPE_NOT_SUPPORTED = 8;
+    const int ERROR_AUTHENTICATION_KEY_NOT_FOUND = 9;
+    const int ERROR_INVALID_ITEMS_REQUEST_MESSAGE = 10;
+    const int ERROR_SESSION_TRANSCRIPT_MISMATCH = 11;
+
+    SecurityHardwareInfoParcel getSecurityHardwareInfo();
+
+    IWritableCredential createCredential(in @utf8InCpp String credentialName,
+                                         in @utf8InCpp String docType);
+    ICredential getCredentialByName(in @utf8InCpp String credentialName,
+                                    in int cipherSuite);
+}
diff --git a/identity/binder/android/security/identity/ICredentialStoreFactory.aidl b/identity/binder/android/security/identity/ICredentialStoreFactory.aidl
new file mode 100644
index 0000000..3164f28
--- /dev/null
+++ b/identity/binder/android/security/identity/ICredentialStoreFactory.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.ICredentialStore;
+
+/**
+ * @hide
+ */
+interface ICredentialStoreFactory {
+    const int CREDENTIAL_STORE_TYPE_DEFAULT = 0;
+    const int CREDENTIAL_STORE_TYPE_DIRECT_ACCESS = 1;
+
+    ICredentialStore getCredentialStore(int credentialStoreType);
+}
diff --git a/identity/binder/android/security/identity/IWritableCredential.aidl b/identity/binder/android/security/identity/IWritableCredential.aidl
new file mode 100644
index 0000000..0e7ca4c
--- /dev/null
+++ b/identity/binder/android/security/identity/IWritableCredential.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.AccessControlProfileParcel;
+import android.security.identity.EntryNamespaceParcel;
+
+/**
+ * @hide
+ */
+interface IWritableCredential {
+    byte[] getCredentialKeyCertificateChain(in byte[] challenge);
+    byte[] personalize(in AccessControlProfileParcel[] accessControlProfiles,
+                       in EntryNamespaceParcel[] entryNamespaces,
+                       in long secureUserId);
+}
diff --git a/identity/binder/android/security/identity/RequestEntryParcel.aidl b/identity/binder/android/security/identity/RequestEntryParcel.aidl
new file mode 100644
index 0000000..77d80c1
--- /dev/null
+++ b/identity/binder/android/security/identity/RequestEntryParcel.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable RequestEntryParcel {
+    @utf8InCpp String name;
+}
diff --git a/identity/binder/android/security/identity/RequestNamespaceParcel.aidl b/identity/binder/android/security/identity/RequestNamespaceParcel.aidl
new file mode 100644
index 0000000..a4fb624
--- /dev/null
+++ b/identity/binder/android/security/identity/RequestNamespaceParcel.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.RequestEntryParcel;
+
+/**
+ * @hide
+ */
+parcelable RequestNamespaceParcel {
+    @utf8InCpp String namespaceName;
+    RequestEntryParcel[] entries;
+}
diff --git a/identity/binder/android/security/identity/ResultEntryParcel.aidl b/identity/binder/android/security/identity/ResultEntryParcel.aidl
new file mode 100644
index 0000000..fb4f9ef
--- /dev/null
+++ b/identity/binder/android/security/identity/ResultEntryParcel.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable ResultEntryParcel {
+    int status;
+    @utf8InCpp String name;
+    byte[] value;
+}
diff --git a/identity/binder/android/security/identity/ResultNamespaceParcel.aidl b/identity/binder/android/security/identity/ResultNamespaceParcel.aidl
new file mode 100644
index 0000000..c9543d9
--- /dev/null
+++ b/identity/binder/android/security/identity/ResultNamespaceParcel.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.ResultEntryParcel;
+
+/**
+ * @hide
+ */
+parcelable ResultNamespaceParcel {
+    @utf8InCpp String namespaceName;
+    ResultEntryParcel[] entries;
+}
diff --git a/identity/binder/android/security/identity/SecurityHardwareInfoParcel.aidl b/identity/binder/android/security/identity/SecurityHardwareInfoParcel.aidl
new file mode 100644
index 0000000..cabaf21
--- /dev/null
+++ b/identity/binder/android/security/identity/SecurityHardwareInfoParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.security.identity;
+
+/**
+ * @hide
+ */
+parcelable SecurityHardwareInfoParcel {
+    boolean directAccess;
+    @utf8InCpp String[] supportedDocTypes;
+}
diff --git a/identity/credstore.rc b/identity/credstore.rc
new file mode 100644
index 0000000..d9e989a
--- /dev/null
+++ b/identity/credstore.rc
@@ -0,0 +1,4 @@
+service credstore /system/bin/credstore /data/misc/credstore
+    class core
+    user credstore
+    group credstore
diff --git a/identity/main.cpp b/identity/main.cpp
new file mode 100644
index 0000000..af03a30
--- /dev/null
+++ b/identity/main.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.security.identity"
+
+#include <filesystem>
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include "CredentialStoreFactory.h"
+
+#include <cppbor.h>
+
+using ::std::string;
+
+using ::android::IPCThreadState;
+using ::android::IServiceManager;
+using ::android::sp;
+using ::android::String16;
+using ::android::base::InitLogging;
+using ::android::base::StderrLogger;
+
+using ::android::security::identity::CredentialStoreFactory;
+
+int main(int argc, char* argv[]) {
+    InitLogging(argv, StderrLogger);
+
+    CHECK(argc == 2) << "A directory must be specified";
+    string data_dir = string(argv[1]);
+    CHECK(chdir(data_dir.c_str()) != -1) << "chdir: " << data_dir << ": " << strerror(errno);
+
+    sp<IServiceManager> sm = ::android::defaultServiceManager();
+    sp<CredentialStoreFactory> factory = new CredentialStoreFactory(data_dir);
+
+    auto ret = sm->addService(String16("android.security.identity"), factory);
+    CHECK(ret == ::android::OK) << "Couldn't register binder service";
+    LOG(ERROR) << "Registered binder service";
+
+    IPCThreadState::self()->joinThreadPool();
+
+    return 0;
+}
diff --git a/keystore/auth_token_table.cpp b/keystore/auth_token_table.cpp
index 6bffa7c..5e6d572 100644
--- a/keystore/auth_token_table.cpp
+++ b/keystore/auth_token_table.cpp
@@ -173,6 +173,60 @@
     return {OK, newest_match->token()};
 }
 
+std::tuple<AuthTokenTable::Error, HardwareAuthToken>
+AuthTokenTable::FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
+                                              int64_t authTokenMaxAgeMillis) {
+    std::vector<uint64_t> sids = {secureUserId};
+    HardwareAuthenticatorType auth_type = HardwareAuthenticatorType::ANY;
+
+    time_t now = clock_function_();
+
+    // challenge-based - the authToken has to contain the given challenge.
+    if (challenge != 0) {
+        auto matching_op = find_if(
+            entries_, [&](Entry& e) { return e.token().challenge == challenge && !e.completed(); });
+        if (matching_op == entries_.end()) {
+            return {AUTH_TOKEN_NOT_FOUND, {}};
+        }
+
+        if (!matching_op->SatisfiesAuth(sids, auth_type)) {
+            return {AUTH_TOKEN_WRONG_SID, {}};
+        }
+
+        if (authTokenMaxAgeMillis > 0) {
+            if (static_cast<int64_t>(matching_op->time_received()) + authTokenMaxAgeMillis <
+                static_cast<int64_t>(now)) {
+                return {AUTH_TOKEN_EXPIRED, {}};
+            }
+        }
+
+        return {OK, matching_op->token()};
+    }
+
+    // Otherwise, no challenge - any authToken younger than the specified maximum
+    // age will do.
+    Entry* newest_match = nullptr;
+    for (auto& entry : entries_) {
+        if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) {
+            newest_match = &entry;
+        }
+    }
+
+    if (newest_match == nullptr) {
+        return {AUTH_TOKEN_NOT_FOUND, {}};
+    }
+
+    if (authTokenMaxAgeMillis > 0) {
+        if (static_cast<int64_t>(newest_match->time_received()) + authTokenMaxAgeMillis <
+            static_cast<int64_t>(now)) {
+            return {AUTH_TOKEN_EXPIRED, {}};
+        }
+    }
+
+    newest_match->UpdateLastUse(now);
+    return {OK, newest_match->token()};
+}
+
 void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) {
     assert(sids);
     for (auto& param : key_info)
diff --git a/keystore/auth_token_table.h b/keystore/auth_token_table.h
index 7b48a6c..86d65de 100644
--- a/keystore/auth_token_table.h
+++ b/keystore/auth_token_table.h
@@ -76,6 +76,10 @@
     std::tuple<Error, HardwareAuthToken> FindAuthorization(const AuthorizationSet& key_info,
                                                            KeyPurpose purpose, uint64_t op_handle);
 
+    std::tuple<Error, HardwareAuthToken>
+    FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
+                                  int64_t authTokenMaxAgeMillis);
+
     /**
      * Mark operation completed.  This allows tokens associated with the specified operation to be
      * superseded by new tokens.
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
index f230043..fcea115 100644
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ b/keystore/binder/android/security/keystore/IKeystoreService.aidl
@@ -84,4 +84,7 @@
     boolean isConfirmationPromptSupported();
     int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
     int listUidsOfAuthBoundKeys(out @utf8InCpp List<String> uids);
+
+    // Called by credstore (and only credstore).
+    byte[] getAuthTokenForCredstore(in long challenge, in long secureUserId, in int authTokenMaxAgeMillis);
 }
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
index 5adc199..41b4109 100644
--- a/keystore/key_store_service.cpp
+++ b/keystore/key_store_service.cpp
@@ -38,6 +38,7 @@
 
 #include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
 #include <android/hardware/keymaster/3.0/IHwKeymasterDevice.h>
+#include <keymasterV4_0/keymaster_utils.h>
 
 #include "defaults.h"
 #include "key_proto_handler.h"
@@ -612,8 +613,6 @@
     const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
     const String16& name, const KeymasterArguments& params, const ::std::vector<uint8_t>& entropy,
     int uid, int flags, int32_t* _aidl_return) {
-    // TODO(jbires): remove this getCallingUid call upon implementation of b/25646100
-    uid_t originalUid = IPCThreadState::self()->getCallingUid();
     uid = getEffectiveUid(uid);
     auto logOnScopeExit = android::base::make_scope_guard([&] {
         if (__android_log_security()) {
@@ -633,9 +632,7 @@
     }
 
     if (containsTag(params.getParameters(), Tag::INCLUDE_UNIQUE_ID)) {
-        // TODO(jbires): remove uid checking upon implementation of b/25646100
-        if (!checkBinderPermission(P_GEN_UNIQUE_ID) ||
-            originalUid != IPCThreadState::self()->getCallingUid()) {
+        if (!checkBinderPermission(P_GEN_UNIQUE_ID)) {
             return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
         }
     }
@@ -946,6 +943,24 @@
     return Status::ok();
 }
 
+Status KeyStoreService::getAuthTokenForCredstore(int64_t challenge, int64_t secureUserId,
+                                                 int32_t authTokenMaxAgeMillis,
+                                                 std::vector<uint8_t>* _aidl_return) {
+    uid_t callingUid = IPCThreadState::self()->getCallingUid();
+    if (callingUid != AID_CREDSTORE) {
+        return Status::fromServiceSpecificError(static_cast<int32_t>(0));
+    }
+
+    auto [err, authToken] = mKeyStore->getAuthTokenTable().FindAuthorizationForCredstore(
+        challenge, secureUserId, authTokenMaxAgeMillis);
+    std::vector<uint8_t> ret;
+    if (err == AuthTokenTable::OK) {
+        ret = android::hardware::keymaster::V4_0::support::authToken2HidlVec(authToken);
+    }
+    *_aidl_return = ret;
+    return Status::ok();
+}
+
 bool isDeviceIdAttestationRequested(const KeymasterArguments& params) {
     const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
     for (size_t i = 0; i < paramsVec.size(); ++i) {
diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h
index a395e6c..8c1d508 100644
--- a/keystore/key_store_service.h
+++ b/keystore/key_store_service.h
@@ -132,6 +132,9 @@
           const ::android::sp<::android::IBinder>& token, int32_t* _aidl_return) override;
     ::android::binder::Status addAuthToken(const ::std::vector<uint8_t>& authToken,
                                            int32_t* _aidl_return) override;
+    ::android::binder::Status
+    getAuthTokenForCredstore(int64_t challenge, int64_t secureUserId, int32_t authTokenMaxAge,
+                             ::std::vector<uint8_t>* _aidl_return) override;
     ::android::binder::Status onUserAdded(int32_t userId, int32_t parentId,
                                           int32_t* _aidl_return) override;
     ::android::binder::Status onUserRemoved(int32_t userId, int32_t* _aidl_return) override;