Add credstore system daemon.

The credstore system daemon is sitting below the Identity Credential
Framework APIs and on top of the Identity Credential HALs. Its main
job is to store credential data and provide a way for applications to
communicate with the secure hardware abstracted by the HAL.

This daemon runs as an unprivileged user, credstore.

The auth-tokens needed by credstore are supplied by keystore and this
CL includes the requisite changes to keystore for this to work.

Bug: 111446262
Test: CTS tests for Framework APIs
Change-Id: Ieb4d59852a143482436a1c418c25ed96e25c0047
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..12c1e72
--- /dev/null
+++ b/identity/credstore.rc
@@ -0,0 +1,5 @@
+service credstore /system/bin/credstore /data/misc/credstore
+    class main
+    user keystore
+    group keystore drmrpc readproc log
+    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
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..5bc5a78 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"
@@ -946,6 +947,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;