Identity Credential: Switch default implementation to use libeic.

Introduce platform-neutral C library ("libeic") which can be used to
implement an Identity Credential Trusted Application/Applet in Secure
Hardware.

The libeic library is intentionally low-level, has no dependencies
(not even libc), uses very little run-time memory (less than 500 bytes
during a provisioning or presentation session), and doesn't
dynamically allocate any memory. Crypto routines are provided by the
library user through a simple crypto interface defined in EicOps.

Also provide an Android-side HAL implementation designed to
communicate with libeic running in Secure Hardware outside
Android. Abstract out communications between HAL and TA in a couple of
SecureHardwareProxy* classes which mimic libeic 1:1.

The default implementation of the HAL is a combination of the
aforementioned HAL using libeic in-process backed by BoringSSL for the
crypto bits.

Test: atest VtsHalIdentityTargetTest
Test: atest android.security.identity.cts
Bug: 170146643
Change-Id: I3bf43fa7fd9362f94023052591801f2094a04607
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 2eb0faa..7f342d0 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -1,3 +1,67 @@
+cc_library_static {
+    name: "android.hardware.identity-libeic-hal-common",
+    vendor_available: true,
+    srcs: [
+        "common/IdentityCredential.cpp",
+        "common/IdentityCredentialStore.cpp",
+        "common/WritableIdentityCredential.cpp",
+    ],
+    export_include_dirs: [
+        "common",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcrypto",
+        "libbinder_ndk",
+        "libkeymaster_messages",
+    ],
+    static_libs: [
+        "libbase",
+        "libcppbor",
+        "libutils",
+        "libsoft_attestation_cert",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
+        "android.hardware.identity-support-lib",
+        "android.hardware.identity-ndk_platform",
+        "android.hardware.keymaster-ndk_platform",
+    ],
+}
+
+cc_library_static {
+    name: "android.hardware.identity-libeic-library",
+    vendor_available: true,
+    srcs: [
+        "libeic/EicCbor.c",
+        "libeic/EicPresentation.c",
+        "libeic/EicProvisioning.c",
+        "EicOpsImpl.cc",
+    ],
+    export_include_dirs: [
+        "libeic",
+    ],
+    cflags: [
+        "-DEIC_COMPILATION",
+        "-Wall",
+        "-Wextra",
+        "-DEIC_DEBUG",
+        // Allow using C2x extensions such as omitting parameter names
+        "-Wno-c2x-extensions",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+    ],
+    static_libs: [
+        "android.hardware.identity-support-lib",
+    ],
+}
+
 cc_binary {
     name: "android.hardware.identity-service.example",
     relative_install_path: "hw",
@@ -7,23 +71,30 @@
     cflags: [
         "-Wall",
         "-Wextra",
+        "-g",
     ],
     shared_libs: [
-        "libbase",
-        "libbinder_ndk",
-        "libcppbor",
-        "libcrypto",
         "liblog",
+        "libcrypto",
+        "libbinder_ndk",
+        "libkeymaster_messages",
+    ],
+    static_libs: [
+        "libbase",
+        "libcppbor",
         "libutils",
+        "libsoft_attestation_cert",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
         "android.hardware.identity-support-lib",
         "android.hardware.identity-ndk_platform",
         "android.hardware.keymaster-ndk_platform",
+        "android.hardware.identity-libeic-hal-common",
+        "android.hardware.identity-libeic-library",
     ],
     srcs: [
-        "IdentityCredential.cpp",
-        "IdentityCredentialStore.cpp",
-        "WritableIdentityCredential.cpp",
-        "Util.cpp",
         "service.cpp",
+        "FakeSecureHardwareProxy.cpp",
     ],
 }
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
new file mode 100644
index 0000000..3f2ec8b
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EicOpsImpl"
+
+#include <optional>
+#include <tuple>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include "EicOps.h"
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+void* eicMemSet(void* s, int c, size_t n) {
+    return memset(s, c, n);
+}
+
+void* eicMemCpy(void* dest, const void* src, size_t n) {
+    return memcpy(dest, src, n);
+}
+
+size_t eicStrLen(const char* s) {
+    return strlen(s);
+}
+
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) {
+    return CRYPTO_memcmp(s1, s2, n);
+}
+
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    HMAC_CTX_init(realCtx);
+    if (HMAC_Init_ex(realCtx, key, keySize, EVP_sha256(), nullptr /* impl */) != 1) {
+        LOG(ERROR) << "Error initializing HMAC_CTX";
+    }
+}
+
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    if (HMAC_Update(realCtx, data, len) != 1) {
+        LOG(ERROR) << "Error updating HMAC_CTX";
+    }
+}
+
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    unsigned int size = 0;
+    if (HMAC_Final(realCtx, digest, &size) != 1) {
+        LOG(ERROR) << "Error finalizing HMAC_CTX";
+    }
+    if (size != EIC_SHA256_DIGEST_SIZE) {
+        LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
+    }
+}
+
+void eicOpsSha256Init(EicSha256Ctx* ctx) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Init(realCtx);
+}
+
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Update(realCtx, data, len);
+}
+
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Final(digest, realCtx);
+}
+
+bool eicOpsRandom(uint8_t* buf, size_t numBytes) {
+    optional<vector<uint8_t>> bytes = ::android::hardware::identity::support::getRandom(numBytes);
+    if (!bytes.has_value()) {
+        return false;
+    }
+    memcpy(buf, bytes.value().data(), numBytes);
+    return true;
+}
+
+bool eicOpsEncryptAes128Gcm(
+        const uint8_t* key,    // Must be 16 bytes
+        const uint8_t* nonce,  // Must be 12 bytes
+        const uint8_t* data,   // May be NULL if size is 0
+        size_t dataSize,
+        const uint8_t* additionalAuthenticationData,  // May be NULL if size is 0
+        size_t additionalAuthenticationDataSize, uint8_t* encryptedData) {
+    vector<uint8_t> cppKey;
+    cppKey.resize(16);
+    memcpy(cppKey.data(), key, 16);
+
+    vector<uint8_t> cppData;
+    cppData.resize(dataSize);
+    if (dataSize > 0) {
+        memcpy(cppData.data(), data, dataSize);
+    }
+
+    vector<uint8_t> cppAAD;
+    cppAAD.resize(additionalAuthenticationDataSize);
+    if (additionalAuthenticationDataSize > 0) {
+        memcpy(cppAAD.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+    }
+
+    vector<uint8_t> cppNonce;
+    cppNonce.resize(12);
+    memcpy(cppNonce.data(), nonce, 12);
+
+    optional<vector<uint8_t>> cppEncryptedData =
+            android::hardware::identity::support::encryptAes128Gcm(cppKey, cppNonce, cppData,
+                                                                   cppAAD);
+    if (!cppEncryptedData.has_value()) {
+        return false;
+    }
+
+    memcpy(encryptedData, cppEncryptedData.value().data(), cppEncryptedData.value().size());
+    return true;
+}
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key,  // Must be 16 bytes
+                            const uint8_t* encryptedData, size_t encryptedDataSize,
+                            const uint8_t* additionalAuthenticationData,
+                            size_t additionalAuthenticationDataSize, uint8_t* data) {
+    vector<uint8_t> keyVec;
+    keyVec.resize(16);
+    memcpy(keyVec.data(), key, 16);
+
+    vector<uint8_t> encryptedDataVec;
+    encryptedDataVec.resize(encryptedDataSize);
+    if (encryptedDataSize > 0) {
+        memcpy(encryptedDataVec.data(), encryptedData, encryptedDataSize);
+    }
+
+    vector<uint8_t> aadVec;
+    aadVec.resize(additionalAuthenticationDataSize);
+    if (additionalAuthenticationDataSize > 0) {
+        memcpy(aadVec.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+    }
+
+    optional<vector<uint8_t>> decryptedDataVec =
+            android::hardware::identity::support::decryptAes128Gcm(keyVec, encryptedDataVec,
+                                                                   aadVec);
+    if (!decryptedDataVec.has_value()) {
+        eicDebug("Error decrypting data");
+        return false;
+    }
+    if (decryptedDataVec.value().size() != encryptedDataSize - 28) {
+        eicDebug("Decrypted data is size %zd, expected %zd", decryptedDataVec.value().size(),
+                 encryptedDataSize - 28);
+        return false;
+    }
+
+    if (decryptedDataVec.value().size() > 0) {
+        memcpy(data, decryptedDataVec.value().data(), decryptedDataVec.value().size());
+    }
+    return true;
+}
+
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                       uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]) {
+    optional<vector<uint8_t>> keyPair = android::hardware::identity::support::createEcKeyPair();
+    if (!keyPair) {
+        eicDebug("Error creating EC keypair");
+        return false;
+    }
+    optional<vector<uint8_t>> privKey =
+            android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair.value());
+    if (!privKey) {
+        eicDebug("Error extracting private key");
+        return false;
+    }
+    if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+        eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+        return false;
+    }
+
+    optional<vector<uint8_t>> pubKey =
+            android::hardware::identity::support::ecKeyPairGetPublicKey(keyPair.value());
+    if (!pubKey) {
+        eicDebug("Error extracting public key");
+        return false;
+    }
+    // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
+    if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
+        eicDebug("Private key is %zd bytes long, expected %zd", pubKey.value().size(),
+                 (size_t)EIC_P256_PRIV_KEY_SIZE + 1);
+        return false;
+    }
+
+    memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+    memcpy(publicKey, pubKey.value().data() + 1, EIC_P256_PUB_KEY_SIZE);
+
+    return true;
+}
+
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+                               size_t challengeSize, const uint8_t* applicationId,
+                               size_t applicationIdSize, bool testCredential, uint8_t* cert,
+                               size_t* certSize) {
+    vector<uint8_t> challengeVec(challengeSize);
+    memcpy(challengeVec.data(), challenge, challengeSize);
+
+    vector<uint8_t> applicationIdVec(applicationIdSize);
+    memcpy(applicationIdVec.data(), applicationId, applicationIdSize);
+
+    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
+            android::hardware::identity::support::createEcKeyPairAndAttestation(
+                    challengeVec, applicationIdVec, testCredential);
+    if (!ret) {
+        eicDebug("Error generating CredentialKey and attestation");
+        return false;
+    }
+
+    // Extract certificate chain.
+    vector<uint8_t> flatChain =
+            android::hardware::identity::support::certificateChainJoin(ret.value().second);
+    if (*certSize < flatChain.size()) {
+        eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+                 flatChain.size());
+        return false;
+    }
+    memcpy(cert, flatChain.data(), flatChain.size());
+    *certSize = flatChain.size();
+
+    // Extract private key.
+    optional<vector<uint8_t>> privKey =
+            android::hardware::identity::support::ecKeyPairGetPrivateKey(ret.value().first);
+    if (!privKey) {
+        eicDebug("Error extracting private key");
+        return false;
+    }
+    if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+        eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+        return false;
+    }
+
+    memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+
+    return true;
+}
+
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                     const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+                     const char* issuerName, const char* subjectName, time_t validityNotBefore,
+                     time_t validityNotAfter, uint8_t* cert,
+                     size_t* certSize) {  // inout
+    vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
+
+    vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+    pubKeyVec[0] = 0x04;
+    memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+    std::string serialDecimal = android::base::StringPrintf("%d", serial);
+
+    optional<vector<uint8_t>> certVec =
+            android::hardware::identity::support::ecPublicKeyGenerateCertificate(
+                    pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
+                    validityNotBefore, validityNotAfter);
+    if (!certVec) {
+        eicDebug("Error generating certificate");
+        return false;
+    }
+
+    if (*certSize < certVec.value().size()) {
+        eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+                 certVec.value().size());
+        return false;
+    }
+    memcpy(cert, certVec.value().data(), certVec.value().size());
+    *certSize = certVec.value().size();
+
+    return true;
+}
+
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                 const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+                 uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+    vector<uint8_t> digestVec(EIC_SHA256_DIGEST_SIZE);
+    memcpy(digestVec.data(), digestOfData, EIC_SHA256_DIGEST_SIZE);
+
+    optional<vector<uint8_t>> derSignature =
+            android::hardware::identity::support::signEcDsaDigest(privKeyVec, digestVec);
+    if (!derSignature) {
+        eicDebug("Error signing data");
+        return false;
+    }
+
+    ECDSA_SIG* sig;
+    const unsigned char* p = derSignature.value().data();
+    sig = d2i_ECDSA_SIG(nullptr, &p, derSignature.value().size());
+    if (sig == nullptr) {
+        eicDebug("Error decoding DER signature");
+        return false;
+    }
+
+    if (BN_bn2binpad(sig->r, signature, 32) != 32) {
+        eicDebug("Error encoding r");
+        return false;
+    }
+    if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) {
+        eicDebug("Error encoding s");
+        return false;
+    }
+
+    return true;
+}
+
+static const uint8_t hbkTest[16] = {0};
+static const uint8_t hbkReal[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential) {
+    if (testCredential) {
+        return hbkTest;
+    }
+    return hbkReal;
+}
+
+bool eicOpsValidateAuthToken(uint64_t /* challenge */, uint64_t /* secureUserId */,
+                             uint64_t /* authenticatorId */, int /* hardwareAuthenticatorType */,
+                             uint64_t /* timeStamp */, const uint8_t* /* mac */,
+                             size_t /* macSize */, uint64_t /* verificationTokenChallenge */,
+                             uint64_t /* verificationTokenTimeStamp */,
+                             int /* verificationTokenSecurityLevel */,
+                             const uint8_t* /* verificationTokenMac */,
+                             size_t /* verificationTokenMacSize */) {
+    // Here's where we would validate the passed-in |authToken| to assure ourselves
+    // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+    //
+    // However this involves calculating the MAC which requires access to the to
+    // a pre-shared key which we don't have...
+    //
+    return true;
+}
+
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+                            size_t* publicKeySize) {
+    vector<uint8_t> chain;
+    chain.resize(x509CertSize);
+    memcpy(chain.data(), x509Cert, x509CertSize);
+    optional<vector<uint8_t>> res =
+            android::hardware::identity::support::certificateChainGetTopMostKey(chain);
+    if (!res) {
+        return false;
+    }
+    if (res.value().size() > *publicKeySize) {
+        eicDebug("Public key size is %zd but buffer only has room for %zd bytes",
+                 res.value().size(), *publicKeySize);
+        return false;
+    }
+    *publicKeySize = res.value().size();
+    memcpy(publicKey, res.value().data(), *publicKeySize);
+    eicDebug("Extracted %zd bytes public key from %zd bytes X.509 cert", *publicKeySize,
+             x509CertSize);
+    return true;
+}
+
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+                                     const uint8_t* publicKey, size_t publicKeySize) {
+    vector<uint8_t> certVec(x509Cert, x509Cert + x509CertSize);
+    vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+    return android::hardware::identity::support::certificateSignedByPublicKey(certVec,
+                                                                              publicKeyVec);
+}
+
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+                                    const uint8_t* signature, size_t signatureSize,
+                                    const uint8_t* publicKey, size_t publicKeySize) {
+    vector<uint8_t> digestVec(digest, digest + digestSize);
+    vector<uint8_t> signatureVec(signature, signature + signatureSize);
+    vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+
+    vector<uint8_t> derSignature;
+    if (!android::hardware::identity::support::ecdsaSignatureCoseToDer(signatureVec,
+                                                                       derSignature)) {
+        LOG(ERROR) << "Error convering signature to DER format";
+        return false;
+    }
+
+    if (!android::hardware::identity::support::checkEcDsaSignature(digestVec, derSignature,
+                                                                   publicKeyVec)) {
+        LOG(ERROR) << "Signature check failed";
+        return false;
+    }
+    return true;
+}
+
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                const uint8_t privateKey[EIC_P256_PUB_KEY_SIZE],
+                uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]) {
+    vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+    pubKeyVec[0] = 0x04;
+    memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+    vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+    optional<vector<uint8_t>> shared =
+            android::hardware::identity::support::ecdh(pubKeyVec, privKeyVec);
+    if (!shared) {
+        LOG(ERROR) << "Error performing ECDH";
+        return false;
+    }
+    if (shared.value().size() != EIC_P256_COORDINATE_SIZE) {
+        LOG(ERROR) << "Unexpected size of shared secret " << shared.value().size() << " expected "
+                   << EIC_P256_COORDINATE_SIZE << " bytes";
+        return false;
+    }
+    memcpy(sharedSecret, shared.value().data(), EIC_P256_COORDINATE_SIZE);
+    return true;
+}
+
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+                size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+                size_t outputSize) {
+    vector<uint8_t> sharedSecretVec(sharedSecretSize);
+    memcpy(sharedSecretVec.data(), sharedSecret, sharedSecretSize);
+    vector<uint8_t> saltVec(saltSize);
+    memcpy(saltVec.data(), salt, saltSize);
+    vector<uint8_t> infoVec(infoSize);
+    memcpy(infoVec.data(), info, infoSize);
+
+    optional<vector<uint8_t>> result = android::hardware::identity::support::hkdf(
+            sharedSecretVec, saltVec, infoVec, outputSize);
+    if (!result) {
+        LOG(ERROR) << "Error performing HKDF";
+        return false;
+    }
+    if (result.value().size() != outputSize) {
+        LOG(ERROR) << "Unexpected size of HKDF " << result.value().size() << " expected "
+                   << outputSize;
+        return false;
+    }
+    memcpy(output, result.value().data(), outputSize);
+    return true;
+}
+
+#ifdef EIC_DEBUG
+
+void eicPrint(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
+}
+
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) {
+    vector<uint8_t> dataVec(dataSize);
+    memcpy(dataVec.data(), data, dataSize);
+    android::hardware::identity::support::hexdump(message, dataVec);
+}
+
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize) {
+    vector<uint8_t> cborDataVec(cborDataSize);
+    memcpy(cborDataVec.data(), cborData, cborDataSize);
+    string str =
+            android::hardware::identity::support::cborPrettyPrint(cborDataVec, maxBStrSize, {});
+    fprintf(stderr, "%s\n", str.c_str());
+}
+
+#endif  // EIC_DEBUG
diff --git a/identity/aidl/default/EicOpsImpl.h b/identity/aidl/default/EicOpsImpl.h
new file mode 100644
index 0000000..333cdce
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Add whatever includes are needed for definitions below.
+//
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Set the following defines to match the implementation of the supplied
+// eicOps*() operations. See EicOps.h for details.
+//
+
+#define EIC_SHA256_CONTEXT_SIZE sizeof(SHA256_CTX)
+
+#define EIC_HMAC_SHA256_CONTEXT_SIZE sizeof(HMAC_CTX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EMBEDDED_IC_H
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
new file mode 100644
index 0000000..de6762f
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FakeSecureHardwareProxy"
+
+#include "FakeSecureHardwareProxy.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include <libeic.h>
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+namespace android::hardware::identity {
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwareProvisioningProxy::FakeSecureHardwareProvisioningProxy() {}
+
+FakeSecureHardwareProvisioningProxy::~FakeSecureHardwareProvisioningProxy() {}
+
+bool FakeSecureHardwareProvisioningProxy::shutdown() {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+    return true;
+}
+
+bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) {
+    LOG(INFO) << "FakeSecureHardwareProvisioningProxy created, sizeof(EicProvisioning): "
+              << sizeof(EicProvisioning);
+    return eicProvisioningInit(&ctx_, testCredential);
+}
+
+// Returns public key certificate.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+    uint8_t publicKeyCert[4096];
+    size_t publicKeyCertSize = sizeof publicKeyCert;
+    if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
+                                            applicationId.data(), applicationId.size(),
+                                            publicKeyCert, &publicKeyCertSize)) {
+        return {};
+    }
+    vector<uint8_t> pubKeyCert(publicKeyCertSize);
+    memcpy(pubKeyCert.data(), publicKeyCert, publicKeyCertSize);
+    return pubKeyCert;
+}
+
+bool FakeSecureHardwareProvisioningProxy::startPersonalization(
+        int accessControlProfileCount, vector<int> entryCounts, const string& docType,
+        size_t expectedProofOfProvisioningSize) {
+    if (!eicProvisioningStartPersonalization(&ctx_, accessControlProfileCount, entryCounts.data(),
+                                             entryCounts.size(), docType.c_str(),
+                                             expectedProofOfProvisioningSize)) {
+        return false;
+    }
+    return true;
+}
+
+// Returns MAC (28 bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addAccessControlProfile(
+        int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+        uint64_t timeoutMillis, uint64_t secureUserId) {
+    vector<uint8_t> mac(28);
+    if (!eicProvisioningAddAccessControlProfile(
+                &ctx_, id, readerCertificate.data(), readerCertificate.size(),
+                userAuthenticationRequired, timeoutMillis, secureUserId, mac.data())) {
+        return {};
+    }
+    return mac;
+}
+
+bool FakeSecureHardwareProvisioningProxy::beginAddEntry(const vector<int>& accessControlProfileIds,
+                                                        const string& nameSpace, const string& name,
+                                                        uint64_t entrySize) {
+    uint8_t scratchSpace[512];
+    return eicProvisioningBeginAddEntry(&ctx_, accessControlProfileIds.data(),
+                                        accessControlProfileIds.size(), nameSpace.c_str(),
+                                        name.c_str(), entrySize, scratchSpace, sizeof scratchSpace);
+}
+
+// Returns encryptedContent.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addEntryValue(
+        const vector<int>& accessControlProfileIds, const string& nameSpace, const string& name,
+        const vector<uint8_t>& content) {
+    vector<uint8_t> eicEncryptedContent;
+    uint8_t scratchSpace[512];
+    eicEncryptedContent.resize(content.size() + 28);
+    if (!eicProvisioningAddEntryValue(
+                &ctx_, accessControlProfileIds.data(), accessControlProfileIds.size(),
+                nameSpace.c_str(), name.c_str(), content.data(), content.size(),
+                eicEncryptedContent.data(), scratchSpace, sizeof scratchSpace)) {
+        return {};
+    }
+    return eicEncryptedContent;
+}
+
+// Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntries() {
+    vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+    if (!eicProvisioningFinishAddingEntries(&ctx_, signatureOfToBeSigned.data())) {
+        return {};
+    }
+    return signatureOfToBeSigned;
+}
+
+// Returns encryptedCredentialKeys (80 bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
+        const string& docType) {
+    vector<uint8_t> encryptedCredentialKeys(80);
+    if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
+                                                encryptedCredentialKeys.data())) {
+        return {};
+    }
+    return encryptedCredentialKeys;
+}
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwarePresentationProxy::FakeSecureHardwarePresentationProxy() {}
+
+FakeSecureHardwarePresentationProxy::~FakeSecureHardwarePresentationProxy() {}
+
+bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string docType,
+                                                     vector<uint8_t> encryptedCredentialKeys) {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
+              << sizeof(EicPresentation);
+    return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
+                               encryptedCredentialKeys.data());
+}
+
+// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+optional<pair<vector<uint8_t>, vector<uint8_t>>>
+FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time_t now) {
+    uint8_t publicKeyCert[512];
+    size_t publicKeyCertSize = sizeof(publicKeyCert);
+    vector<uint8_t> signingKeyBlob(60);
+
+    if (!eicPresentationGenerateSigningKeyPair(&ctx_, docType.c_str(), now, publicKeyCert,
+                                               &publicKeyCertSize, signingKeyBlob.data())) {
+        return {};
+    }
+
+    vector<uint8_t> cert;
+    cert.resize(publicKeyCertSize);
+    memcpy(cert.data(), publicKeyCert, publicKeyCertSize);
+
+    return std::make_pair(cert, signingKeyBlob);
+}
+
+// Returns private key
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::createEphemeralKeyPair() {
+    vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
+    if (!eicPresentationCreateEphemeralKeyPair(&ctx_, priv.data())) {
+        return {};
+    }
+    return priv;
+}
+
+optional<uint64_t> FakeSecureHardwarePresentationProxy::createAuthChallenge() {
+    uint64_t challenge;
+    if (!eicPresentationCreateAuthChallenge(&ctx_, &challenge)) {
+        return {};
+    }
+    return challenge;
+}
+
+bool FakeSecureHardwarePresentationProxy::shutdown() {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+    return true;
+}
+
+bool FakeSecureHardwarePresentationProxy::pushReaderCert(const vector<uint8_t>& certX509) {
+    return eicPresentationPushReaderCert(&ctx_, certX509.data(), certX509.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::validateRequestMessage(
+        const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& requestMessage,
+        int coseSignAlg, const vector<uint8_t>& readerSignatureOfToBeSigned) {
+    return eicPresentationValidateRequestMessage(
+            &ctx_, sessionTranscript.data(), sessionTranscript.size(), requestMessage.data(),
+            requestMessage.size(), coseSignAlg, readerSignatureOfToBeSigned.data(),
+            readerSignatureOfToBeSigned.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::setAuthToken(
+        uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+        int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+        uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+        int verificationTokenSecurityLevel, const vector<uint8_t>& verificationTokenMac) {
+    return eicPresentationSetAuthToken(&ctx_, challenge, secureUserId, authenticatorId,
+                                       hardwareAuthenticatorType, timeStamp, mac.data(), mac.size(),
+                                       verificationTokenChallenge, verificationTokenTimestamp,
+                                       verificationTokenSecurityLevel, verificationTokenMac.data(),
+                                       verificationTokenMac.size());
+}
+
+optional<bool> FakeSecureHardwarePresentationProxy::validateAccessControlProfile(
+        int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+        int timeoutMillis, uint64_t secureUserId, const vector<uint8_t>& mac) {
+    bool accessGranted = false;
+    if (!eicPresentationValidateAccessControlProfile(&ctx_, id, readerCertificate.data(),
+                                                     readerCertificate.size(),
+                                                     userAuthenticationRequired, timeoutMillis,
+                                                     secureUserId, mac.data(), &accessGranted)) {
+        return {};
+    }
+    return accessGranted;
+}
+
+bool FakeSecureHardwarePresentationProxy::startRetrieveEntries() {
+    return eicPresentationStartRetrieveEntries(&ctx_);
+}
+
+bool FakeSecureHardwarePresentationProxy::calcMacKey(
+        const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& readerEphemeralPublicKey,
+        const vector<uint8_t>& signingKeyBlob, const string& docType,
+        unsigned int numNamespacesWithValues, size_t expectedProofOfProvisioningSize) {
+    if (signingKeyBlob.size() != 60) {
+        eicDebug("Unexpected size %zd of signingKeyBlob, expected 60", signingKeyBlob.size());
+        return false;
+    }
+    return eicPresentationCalcMacKey(&ctx_, sessionTranscript.data(), sessionTranscript.size(),
+                                     readerEphemeralPublicKey.data(), signingKeyBlob.data(),
+                                     docType.c_str(), numNamespacesWithValues,
+                                     expectedProofOfProvisioningSize);
+}
+
+AccessCheckResult FakeSecureHardwarePresentationProxy::startRetrieveEntryValue(
+        const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+        int32_t entrySize, const vector<int32_t>& accessControlProfileIds) {
+    uint8_t scratchSpace[512];
+    EicAccessCheckResult result = eicPresentationStartRetrieveEntryValue(
+            &ctx_, nameSpace.c_str(), name.c_str(), newNamespaceNumEntries, entrySize,
+            accessControlProfileIds.data(), accessControlProfileIds.size(), scratchSpace,
+            sizeof scratchSpace);
+    switch (result) {
+        case EIC_ACCESS_CHECK_RESULT_OK:
+            return AccessCheckResult::kOk;
+        case EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES:
+            return AccessCheckResult::kNoAccessControlProfiles;
+        case EIC_ACCESS_CHECK_RESULT_FAILED:
+            return AccessCheckResult::kFailed;
+        case EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED:
+            return AccessCheckResult::kUserAuthenticationFailed;
+        case EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED:
+            return AccessCheckResult::kReaderAuthenticationFailed;
+    }
+    eicDebug("Unknown result with code %d, returning kFailed", (int)result);
+    return AccessCheckResult::kFailed;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::retrieveEntryValue(
+        const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+        const vector<int32_t>& accessControlProfileIds) {
+    uint8_t scratchSpace[512];
+    vector<uint8_t> content;
+    content.resize(encryptedContent.size() - 28);
+    if (!eicPresentationRetrieveEntryValue(
+                &ctx_, encryptedContent.data(), encryptedContent.size(), content.data(),
+                nameSpace.c_str(), name.c_str(), accessControlProfileIds.data(),
+                accessControlProfileIds.size(), scratchSpace, sizeof scratchSpace)) {
+        return {};
+    }
+    return content;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval() {
+    vector<uint8_t> mac(32);
+    size_t macSize = 32;
+    if (!eicPresentationFinishRetrieval(&ctx_, mac.data(), &macSize)) {
+        return {};
+    }
+    mac.resize(macSize);
+    return mac;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
+        const string& docType, size_t proofOfDeletionCborSize) {
+    vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+    if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), proofOfDeletionCborSize,
+                                         signatureOfToBeSigned.data())) {
+        return {};
+    }
+    return signatureOfToBeSigned;
+}
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h
new file mode 100644
index 0000000..b858dd4
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+
+#include <libeic/libeic.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace android::hardware::identity {
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningProxy {
+  public:
+    FakeSecureHardwareProvisioningProxy();
+    virtual ~FakeSecureHardwareProvisioningProxy();
+
+    bool initialize(bool testCredential) override;
+
+    bool shutdown() override;
+
+    // Returns public key certificate.
+    optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+                                                  const vector<uint8_t>& applicationId) override;
+
+    bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+                              const string& docType,
+                              size_t expectedProofOfProvisioningSize) override;
+
+    // Returns MAC (28 bytes).
+    optional<vector<uint8_t>> addAccessControlProfile(int id,
+                                                      const vector<uint8_t>& readerCertificate,
+                                                      bool userAuthenticationRequired,
+                                                      uint64_t timeoutMillis,
+                                                      uint64_t secureUserId) override;
+
+    bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+                       const string& name, uint64_t entrySize) override;
+
+    // Returns encryptedContent.
+    optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+                                            const string& nameSpace, const string& name,
+                                            const vector<uint8_t>& content) override;
+
+    // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+    optional<vector<uint8_t>> finishAddingEntries() override;
+
+    // Returns encryptedCredentialKeys (80 bytes).
+    optional<vector<uint8_t>> finishGetCredentialData(const string& docType) override;
+
+  protected:
+    EicProvisioning ctx_;
+};
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationProxy {
+  public:
+    FakeSecureHardwarePresentationProxy();
+    virtual ~FakeSecureHardwarePresentationProxy();
+
+    bool initialize(bool testCredential, string docType,
+                    vector<uint8_t> encryptedCredentialKeys) override;
+
+    // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+    optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+                                                                            time_t now) override;
+
+    // Returns private key
+    optional<vector<uint8_t>> createEphemeralKeyPair() override;
+
+    optional<uint64_t> createAuthChallenge() override;
+
+    bool startRetrieveEntries() override;
+
+    bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                      int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+                      uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+                      int verificationTokenSecurityLevel,
+                      const vector<uint8_t>& verificationTokenMac) override;
+
+    bool pushReaderCert(const vector<uint8_t>& certX509) override;
+
+    optional<bool> validateAccessControlProfile(int id, const vector<uint8_t>& readerCertificate,
+                                                bool userAuthenticationRequired, int timeoutMillis,
+                                                uint64_t secureUserId,
+                                                const vector<uint8_t>& mac) override;
+
+    bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+                                const vector<uint8_t>& requestMessage, int coseSignAlg,
+                                const vector<uint8_t>& readerSignatureOfToBeSigned) override;
+
+    bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+                    const vector<uint8_t>& readerEphemeralPublicKey,
+                    const vector<uint8_t>& signingKeyBlob, const string& docType,
+                    unsigned int numNamespacesWithValues,
+                    size_t expectedProofOfProvisioningSize) override;
+
+    AccessCheckResult startRetrieveEntryValue(
+            const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+            int32_t entrySize, const vector<int32_t>& accessControlProfileIds) override;
+
+    optional<vector<uint8_t>> retrieveEntryValue(
+            const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+            const vector<int32_t>& accessControlProfileIds) override;
+
+    optional<vector<uint8_t>> finishRetrieval() override;
+
+    optional<vector<uint8_t>> deleteCredential(const string& docType,
+                                               size_t proofOfDeletionCborSize) override;
+
+    bool shutdown() override;
+
+  protected:
+    EicPresentation ctx_;
+};
+
+// Factory implementation.
+//
+class FakeSecureHardwareProxyFactory : public SecureHardwareProxyFactory {
+  public:
+    FakeSecureHardwareProxyFactory() {}
+    virtual ~FakeSecureHardwareProxyFactory() {}
+
+    sp<SecureHardwareProvisioningProxy> createProvisioningProxy() override {
+        return new FakeSecureHardwareProvisioningProxy();
+    }
+
+    sp<SecureHardwarePresentationProxy> createPresentationProxy() override {
+        return new FakeSecureHardwarePresentationProxy();
+    }
+};
+
+}  // namespace android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
deleted file mode 100644
index 66b9f13..0000000
--- a/identity/aidl/default/Util.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Util"
-
-#include "Util.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace aidl::android::hardware::identity {
-
-using namespace ::android::hardware::identity;
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
-    return hardwareBoundKey;
-}
-
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
-    cppbor::Map map;
-    map.add("id", profile.id);
-
-    if (profile.readerCertificate.encodedCertificate.size() > 0) {
-        map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate.encodedCertificate));
-    }
-
-    if (profile.userAuthenticationRequired) {
-        map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
-        map.add("timeoutMillis", profile.timeoutMillis);
-        map.add("secureUserId", profile.secureUserId);
-    }
-
-    return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
-        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
-    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return {};
-    }
-    optional<vector<uint8_t>> macO =
-            support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
-    if (!macO) {
-        return {};
-    }
-    return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
-                                        const vector<uint8_t>& storageKey) {
-    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
-    if (profile.mac.size() < support::kAesGcmIvSize) {
-        return false;
-    }
-    vector<uint8_t> nonce =
-            vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
-    optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
-    if (!mac) {
-        return false;
-    }
-    if (mac.value() != profile.mac) {
-        return false;
-    }
-    return true;
-}
-
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
-                                          const vector<int32_t> accessControlProfileIds) {
-    cppbor::Map map;
-    map.add("Namespace", nameSpace);
-    map.add("Name", name);
-
-    cppbor::Array acpIds;
-    for (auto id : accessControlProfileIds) {
-        acpIds.add(id);
-    }
-    map.add("AccessControlProfileIds", std::move(acpIds));
-    return map.encode();
-}
-
-}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
deleted file mode 100644
index 9fccba2..0000000
--- a/identity/aidl/default/Util.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_UTIL_H
-#define ANDROID_HARDWARE_IDENTITY_UTIL_H
-
-#include <aidl/android/hardware/identity/BnIdentityCredential.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <optional>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
-        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
-                                        const vector<uint8_t>& storageKey);
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
-                                          const vector<int32_t> accessControlProfileIds);
-
-}  // namespace aidl::android::hardware::identity
-
-#endif  // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
similarity index 65%
rename from identity/aidl/default/IdentityCredential.cpp
rename to identity/aidl/default/common/IdentityCredential.cpp
index dfcd4f5..270fcfa 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -18,7 +18,6 @@
 
 #include "IdentityCredential.h"
 #include "IdentityCredentialStore.h"
-#include "Util.h"
 
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 
@@ -30,6 +29,8 @@
 #include <cppbor.h>
 #include <cppbor_parse.h>
 
+#include "FakeSecureHardwareProxy.h"
+
 namespace aidl::android::hardware::identity {
 
 using ::aidl::android::hardware::keymaster::Timestamp;
@@ -69,40 +70,17 @@
     docType_ = docTypeItem->value();
     testCredential_ = testCredentialItem->value();
 
-    vector<uint8_t> hardwareBoundKey;
-    if (testCredential_) {
-        hardwareBoundKey = support::getTestHardwareBoundKey();
-    } else {
-        hardwareBoundKey = getHardwareBoundKey();
-    }
-
     const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-    const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
-    optional<vector<uint8_t>> decryptedCredentialKeys =
-            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
-    if (!decryptedCredentialKeys) {
-        LOG(ERROR) << "Error decrypting CredentialKeys";
+
+    if (encryptedCredentialKeys.size() != 80) {
+        LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
         return IIdentityCredentialStore::STATUS_INVALID_DATA;
     }
 
-    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
-    if (dckItem == nullptr) {
-        LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
+    if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
+        LOG(ERROR) << "hwProxy->initialize failed";
+        return false;
     }
-    const cppbor::Array* dckArrayItem = dckItem->asArray();
-    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
-        LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
-    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
-    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
-        LOG(ERROR) << "CredentialKeys unexpected item types";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    storageKey_ = storageKeyItem->value();
-    credentialPrivKey_ = credentialPrivKeyItem->value();
 
     return IIdentityCredentialStore::STATUS_OK;
 }
@@ -110,12 +88,20 @@
 ndk::ScopedAStatus IdentityCredential::deleteCredential(
         vector<uint8_t>* outProofOfDeletionSignature) {
     cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
-    vector<uint8_t> proofOfDeletion = array.encode();
+    vector<uint8_t> proofOfDeletionCbor = array.encode();
+    vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
 
-    optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
-                                                                 proofOfDeletion,  // payload
-                                                                 {},               // additionalData
-                                                                 {});  // certificateChain
+    optional<vector<uint8_t>> signatureOfToBeSigned =
+            hwProxy_->deleteCredential(docType_, proofOfDeletionCbor.size());
+    if (!signatureOfToBeSigned) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
+    }
+
+    optional<vector<uint8_t>> signature =
+            support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+                                                proofOfDeletionCbor,  // data
+                                                {});                  // certificateChain
     if (!signature) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
@@ -126,22 +112,28 @@
 }
 
 ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
-    optional<vector<uint8_t>> kp = support::createEcKeyPair();
-    if (!kp) {
+    optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
+    if (!ephemeralPriv) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
+                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key"));
+    }
+    optional<vector<uint8_t>> keyPair = support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
+    if (!keyPair) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key-pair"));
     }
 
     // Stash public key of this key-pair for later check in startRetrieval().
-    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
+    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
     if (!publicKey) {
+        LOG(ERROR) << "Error getting public part of ephemeral key pair";
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
                 "Error getting public part of ephemeral key pair"));
     }
     ephemeralPublicKey_ = publicKey.value();
 
-    *outKeyPair = kp.value();
+    *outKeyPair = keyPair.value();
     return ndk::ScopedAStatus::ok();
 }
 
@@ -152,109 +144,15 @@
 }
 
 ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
-    uint64_t challenge = 0;
-    while (challenge == 0) {
-        optional<vector<uint8_t>> bytes = support::getRandom(8);
-        if (!bytes) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED,
-                    "Error getting random data for challenge"));
-        }
-
-        challenge = 0;
-        for (size_t n = 0; n < bytes.value().size(); n++) {
-            challenge |= ((bytes.value())[n] << (n * 8));
-        }
+    optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
+    if (!challenge) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
     }
-
-    *outChallenge = challenge;
-    authChallenge_ = challenge;
+    *outChallenge = challenge.value();
     return ndk::ScopedAStatus::ok();
 }
 
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
-                               const vector<uint8_t>& readerCertificateChain) {
-    optional<vector<uint8_t>> acpPubKey =
-            support::certificateChainGetTopMostKey(profile.readerCertificate.encodedCertificate);
-    if (!acpPubKey) {
-        LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
-        return false;
-    }
-
-    optional<vector<vector<uint8_t>>> certificatesInChain =
-            support::certificateChainSplit(readerCertificateChain);
-    if (!certificatesInChain) {
-        LOG(ERROR) << "Error splitting readerCertificateChain";
-        return false;
-    }
-    for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
-        optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
-        if (!certPubKey) {
-            LOG(ERROR)
-                    << "Error extracting public key from certificate in chain presented by reader";
-            return false;
-        }
-        if (acpPubKey.value() == certPubKey.value()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
-                             const VerificationToken& verificationToken,
-                             const HardwareAuthToken& authToken, uint64_t authChallenge) {
-    if (profile.secureUserId != authToken.userId) {
-        LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
-                   << ") differs from userId in authToken (" << authToken.userId << ")";
-        return false;
-    }
-
-    if (verificationToken.timestamp.milliSeconds == 0) {
-        LOG(ERROR) << "VerificationToken is not set";
-        return false;
-    }
-    if (authToken.timestamp.milliSeconds == 0) {
-        LOG(ERROR) << "AuthToken is not set";
-        return false;
-    }
-
-    if (profile.timeoutMillis == 0) {
-        if (authToken.challenge == 0) {
-            LOG(ERROR) << "No challenge in authToken";
-            return false;
-        }
-
-        if (authToken.challenge != int64_t(authChallenge)) {
-            LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
-                       << "doesn't match the challenge we created (" << authChallenge << ")";
-            return false;
-        }
-        return true;
-    }
-
-    // Timeout-based user auth follows. The verification token conveys what the
-    // time is right now in the environment which generated the auth token. This
-    // is what makes it possible to do timeout-based checks.
-    //
-    const Timestamp now = verificationToken.timestamp;
-    if (authToken.timestamp.milliSeconds > now.milliSeconds) {
-        LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
-                   << ") is in the future (now: " << now.milliSeconds << ")";
-        return false;
-    }
-    if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
-        LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
-                   << profile.timeoutMillis << " = "
-                   << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
-                   << ") is in the past (now: " << now.milliSeconds << ")";
-        return false;
-    }
-    return true;
-}
-
 ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
         const vector<RequestNamespace>& requestNamespaces) {
     requestNamespaces_ = requestNamespaces;
@@ -284,6 +182,7 @@
     }
     if (numStartRetrievalCalls_ > 0) {
         if (sessionTranscript_ != sessionTranscript) {
+            LOG(ERROR) << "Session Transcript changed";
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
                     "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
@@ -291,6 +190,40 @@
     }
     sessionTranscript_ = sessionTranscript;
 
+    // This resets various state in the TA...
+    if (!hwProxy_->startRetrieveEntries()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+    }
+
+    optional<vector<uint8_t>> signatureOfToBeSigned;
+    if (readerSignature.size() > 0) {
+        signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
+        if (!signatureOfToBeSigned) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                    "Error extracting signatureOfToBeSigned from COSE_Sign1"));
+        }
+    }
+
+    // Feed the auth token to secure hardware.
+    if (!hwProxy_->setAuthToken(authToken.challenge, authToken.userId, authToken.authenticatorId,
+                                int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
+                                authToken.mac, verificationToken_.challenge,
+                                verificationToken_.timestamp.milliSeconds,
+                                int(verificationToken_.securityLevel), verificationToken_.mac)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
+    }
+
+    // We'll be feeding ACPs interleaved with certificates from the reader
+    // certificate chain...
+    vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
+
+    // ... and we'll use those ACPs to build up a 32-bit mask indicating which
+    // of the possible 32 ACPs grants access.
+    uint32_t accessControlProfileMask = 0;
+
     // If there is a signature, validate that it was made with the top-most key in the
     // certificate chain embedded in the COSE_Sign1 structure.
     optional<vector<uint8_t>> readerCertificateChain;
@@ -302,45 +235,113 @@
                     "Unable to get reader certificate chain from COSE_Sign1"));
         }
 
-        if (!support::certificateChainValidate(readerCertificateChain.value())) {
+        // First, feed all the reader certificates to the secure hardware. We start
+        // at the end..
+        optional<vector<vector<uint8_t>>> splitCerts =
+                support::certificateChainSplit(readerCertificateChain.value());
+        if (!splitCerts || splitCerts.value().size() == 0) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "Error validating reader certificate chain"));
+                    "Error splitting certificate chain from COSE_Sign1"));
+        }
+        for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
+            const vector<uint8_t>& x509Cert = splitCerts.value()[n];
+            if (!hwProxy_->pushReaderCert(x509Cert)) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        StringPrintf("Error validating reader certificate %zd", n).c_str()));
+            }
+
+            // If we have ACPs for that particular certificate, send them to the
+            // TA right now...
+            //
+            // Remember in this case certificate equality is done by comparing public keys,
+            // not bitwise comparison of the certificates.
+            //
+            optional<vector<uint8_t>> x509CertPubKey =
+                    support::certificateChainGetTopMostKey(x509Cert);
+            if (!x509CertPubKey) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_FAILED,
+                        StringPrintf("Error getting public key from reader certificate %zd", n)
+                                .c_str()));
+            }
+            vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
+            while (it != remainingAcps.end()) {
+                const SecureAccessControlProfile& profile = *it;
+                if (profile.readerCertificate.encodedCertificate.size() == 0) {
+                    ++it;
+                    continue;
+                }
+                optional<vector<uint8_t>> profilePubKey = support::certificateChainGetTopMostKey(
+                        profile.readerCertificate.encodedCertificate);
+                if (!profilePubKey) {
+                    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                            IIdentityCredentialStore::STATUS_FAILED,
+                            "Error getting public key from profile"));
+                }
+                if (profilePubKey.value() == x509CertPubKey.value()) {
+                    optional<bool> res = hwProxy_->validateAccessControlProfile(
+                            profile.id, profile.readerCertificate.encodedCertificate,
+                            profile.userAuthenticationRequired, profile.timeoutMillis,
+                            profile.secureUserId, profile.mac);
+                    if (!res) {
+                        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                                "Error validating access control profile"));
+                    }
+                    if (res.value()) {
+                        accessControlProfileMask |= (1 << profile.id);
+                    }
+                    it = remainingAcps.erase(it);
+                } else {
+                    ++it;
+                }
+            }
         }
 
-        optional<vector<uint8_t>> readerPublicKey =
-                support::certificateChainGetTopMostKey(readerCertificateChain.value());
-        if (!readerPublicKey) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "Unable to get public key from reader certificate chain"));
-        }
-
-        const vector<uint8_t>& itemsRequestBytes = itemsRequest;
-        vector<uint8_t> encodedReaderAuthentication =
-                cppbor::Array()
-                        .add("ReaderAuthentication")
-                        .add(std::move(sessionTranscriptItem))
-                        .add(cppbor::Semantic(24, itemsRequestBytes))
-                        .encode();
-        vector<uint8_t> encodedReaderAuthenticationBytes =
-                cppbor::Semantic(24, encodedReaderAuthentication).encode();
-        if (!support::coseCheckEcDsaSignature(readerSignature,
-                                              encodedReaderAuthenticationBytes,  // detached content
-                                              readerPublicKey.value())) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "readerSignature check failed"));
+        // ... then pass the request message and have the TA check it's signed by the
+        // key in last certificate we pushed.
+        if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 && readerSignature.size() > 0) {
+            optional<vector<uint8_t>> tbsSignature = support::coseSignGetSignature(readerSignature);
+            if (!tbsSignature) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "Error extracting toBeSigned from COSE_Sign1"));
+            }
+            optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
+            if (!coseSignAlg) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "Error extracting signature algorithm from COSE_Sign1"));
+            }
+            if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
+                                                  coseSignAlg.value(), tbsSignature.value())) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "readerMessage is not signed by top-level certificate"));
+            }
         }
     }
 
-    // Here's where we would validate the passed-in |authToken| to assure ourselves
-    // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
-    //
-    // However this involves calculating the MAC. However this requires access
-    // to the key needed to a pre-shared key which we don't have...
-    //
+    // Feed remaining access control profiles...
+    for (const SecureAccessControlProfile& profile : remainingAcps) {
+        optional<bool> res = hwProxy_->validateAccessControlProfile(
+                profile.id, profile.readerCertificate.encodedCertificate,
+                profile.userAuthenticationRequired, profile.timeoutMillis, profile.secureUserId,
+                profile.mac);
+        if (!res) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Error validating access control profile"));
+        }
+        if (res.value()) {
+            accessControlProfileMask |= (1 << profile.id);
+        }
+    }
 
+    // TODO: move this check to the TA
+#if 1
     // To prevent replay-attacks, we check that the public part of the ephemeral
     // key we previously created, is present in the DeviceEngagement part of
     // SessionTranscript as a COSE_Key, in uncompressed form.
@@ -364,6 +365,7 @@
                     "SessionTranscript (make sure leading zeroes are not used)"));
         }
     }
+#endif
 
     // itemsRequest: If non-empty, contains request data that may be signed by the
     // reader.  The content can be defined in the way appropriate for the
@@ -463,30 +465,6 @@
         }
     }
 
-    // Validate all the access control profiles in the requestData.
-    bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
-    for (const auto& profile : accessControlProfiles) {
-        if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
-            LOG(ERROR) << "Error checking MAC for profile";
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Error checking MAC for profile"));
-        }
-        int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
-        if (profile.userAuthenticationRequired) {
-            if (!haveAuthToken ||
-                !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) {
-                accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
-            }
-        } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
-            if (!readerCertificateChain ||
-                !checkReaderAuthentication(profile, readerCertificateChain.value())) {
-                accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
-            }
-        }
-        profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
-    }
-
     deviceNameSpacesMap_ = cppbor::Map();
     currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
 
@@ -496,8 +474,36 @@
     itemsRequest_ = itemsRequest;
     signingKeyBlob_ = signingKeyBlob;
 
-    // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time.
-    expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize();
+    // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
+    calcDeviceNameSpacesSize(accessControlProfileMask);
+
+    // Count the number of non-empty namespaces
+    size_t numNamespacesWithValues = 0;
+    for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
+        if (expectedNumEntriesPerNamespace_[n] > 0) {
+            numNamespacesWithValues += 1;
+        }
+    }
+
+    // Finally, pass info so the HMAC key can be derived and the TA can start
+    // creating the DeviceNameSpaces CBOR...
+    if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) {
+        // We expect the reader ephemeral public key to be same size and curve
+        // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
+        // won't work. So its length should be 65 bytes and it should be
+        // starting with 0x04.
+        if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED,
+                    "Reader public key is not in expected format"));
+        }
+        vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
+        if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
+                                  numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+        }
+    }
 
     numStartRetrievalCalls_ += 1;
     return ndk::ScopedAStatus::ok();
@@ -520,7 +526,7 @@
     return 1 + cborNumBytesForLength(value.size()) + value.size();
 }
 
-size_t IdentityCredential::calcDeviceNameSpacesSize() {
+void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileMask) {
     /*
      * This is how DeviceNameSpaces is defined:
      *
@@ -539,7 +545,7 @@
      * encoded.
      */
     size_t ret = 0;
-    size_t numNamespacesWithValues = 0;
+    vector<unsigned int> numEntriesPerNamespace;
     for (const RequestNamespace& rns : requestNamespaces_) {
         vector<RequestDataItem> itemsToInclude;
 
@@ -562,13 +568,9 @@
             //
             bool authorized = false;
             for (auto id : rdi.accessControlProfileIds) {
-                auto it = profileIdToAccessCheckResult_.find(id);
-                if (it != profileIdToAccessCheckResult_.end()) {
-                    int accessControlForProfile = it->second;
-                    if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
-                        authorized = true;
-                        break;
-                    }
+                if (accessControlProfileMask & (1 << id)) {
+                    authorized = true;
+                    break;
                 }
             }
             if (!authorized) {
@@ -578,7 +580,10 @@
             itemsToInclude.push_back(rdi);
         }
 
-        // If no entries are to be in the namespace, we don't include it...
+        numEntriesPerNamespace.push_back(itemsToInclude.size());
+
+        // If no entries are to be in the namespace, we don't include it in
+        // the CBOR...
         if (itemsToInclude.size() == 0) {
             continue;
         }
@@ -597,15 +602,14 @@
             // that.
             ret += item.size;
         }
-
-        numNamespacesWithValues++;
     }
 
-    // Now that we now the nunber of namespaces with values, we know how many
+    // Now that we know the number of namespaces with values, we know how many
     // bytes the DeviceNamespaces map in the beginning is going to take up.
-    ret += 1 + cborNumBytesForLength(numNamespacesWithValues);
+    ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
 
-    return ret;
+    expectedDeviceNameSpacesSize_ = ret;
+    expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
 }
 
 ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
@@ -626,9 +630,11 @@
                 "No more name spaces left to go through"));
     }
 
+    bool newNamespace;
     if (currentNameSpace_ == "") {
         // First call.
         currentNameSpace_ = nameSpace;
+        newNamespace = true;
     }
 
     if (nameSpace == currentNameSpace_) {
@@ -655,6 +661,7 @@
 
         requestCountsRemaining_.erase(requestCountsRemaining_.begin());
         currentNameSpace_ = nameSpace;
+        newNamespace = true;
     }
 
     // It's permissible to have an empty itemsRequest... but if non-empty you can
@@ -674,35 +681,52 @@
         }
     }
 
-    // Enforce access control.
-    //
-    // Access is granted if at least one of the profiles grants access.
-    //
-    // If an item is configured without any profiles, access is denied.
-    //
-    int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
-    for (auto id : accessControlProfileIds) {
-        auto search = profileIdToAccessCheckResult_.find(id);
-        if (search == profileIdToAccessCheckResult_.end()) {
+    unsigned int newNamespaceNumEntries = 0;
+    if (newNamespace) {
+        if (expectedNumEntriesPerNamespace_.size() == 0) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Requested entry with unvalidated profile id"));
+                    "No more populated name spaces left to go through"));
         }
-        int accessControlForProfile = search->second;
-        if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
-            accessControl = IIdentityCredentialStore::STATUS_OK;
-            break;
-        }
-        accessControl = accessControlForProfile;
-    }
-    if (accessControl != IIdentityCredentialStore::STATUS_OK) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                int(accessControl), "Access control check failed"));
+        newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
+        expectedNumEntriesPerNamespace_.erase(expectedNumEntriesPerNamespace_.begin());
     }
 
-    entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+    // Access control is enforced in the secure hardware.
+    //
+    // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
+    // consolidate).
+    //
+    AccessCheckResult res = hwProxy_->startRetrieveEntryValue(
+            nameSpace, name, newNamespaceNumEntries, entrySize, accessControlProfileIds);
+    switch (res) {
+        case AccessCheckResult::kOk:
+            /* Do nothing. */
+            break;
+        case AccessCheckResult::kFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED,
+                    "Access control check failed (failed)"));
+            break;
+        case AccessCheckResult::kNoAccessControlProfiles:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
+                    "Access control check failed (no access control profiles)"));
+            break;
+        case AccessCheckResult::kUserAuthenticationFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
+                    "Access control check failed (user auth)"));
+            break;
+        case AccessCheckResult::kReaderAuthenticationFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
+                    "Access control check failed (reader auth)"));
+            break;
+    }
 
     currentName_ = name;
+    currentAccessControlProfileIds_ = accessControlProfileIds;
     entryRemainingBytes_ = entrySize;
     entryValue_.resize(0);
 
@@ -711,8 +735,8 @@
 
 ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent,
                                                           vector<uint8_t>* outContent) {
-    optional<vector<uint8_t>> content =
-            support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+    optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
+            encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_);
     if (!content) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
@@ -777,28 +801,14 @@
     optional<vector<uint8_t>> mac;
     if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
         readerPublicKey_.size() > 0) {
-        vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
-        optional<vector<uint8_t>> signingKey =
-                support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
-        if (!signingKey) {
+        optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
+        if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Error decrypting signingKeyBlob"));
+                    "Error generating digestToBeMaced"));
         }
-
-        vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
-        optional<vector<uint8_t>> eMacKey =
-                support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
-        if (!eMacKey) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
-        }
-        mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
-                               eMacKey.value());
-        if (!mac) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
-        }
+        // Now construct COSE_Mac0 from the returned MAC...
+        mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
     }
 
     *outMac = mac.value_or(vector<uint8_t>({}));
@@ -808,56 +818,18 @@
 
 ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
         vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
-    string serialDecimal = "1";
-    string issuer = "Android Identity Credential Key";
-    string subject = "Android Identity Credential Authentication Key";
-    time_t validityNotBefore = time(nullptr);
-    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
-    optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
-    if (!signingKeyPKCS8) {
+    time_t now = time(NULL);
+    optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
+            hwProxy_->generateSigningKeyPair(docType_, now);
+    if (!pair) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
     }
 
-    optional<vector<uint8_t>> signingPublicKey =
-            support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
-    if (!signingPublicKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of signingKey"));
-    }
-
-    optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
-    if (!signingKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting private part of signingKey"));
-    }
-
-    optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
-            signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
-            validityNotBefore, validityNotAfter);
-    if (!certificate) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
-    }
-
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
-    }
-    vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
-    optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
-            storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
-    if (!encryptedSigningKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
-    }
-    *outSigningKeyBlob = encryptedSigningKey.value();
     *outSigningKeyCertificate = Certificate();
-    outSigningKeyCertificate->encodedCertificate = certificate.value();
+    outSigningKeyCertificate->encodedCertificate = pair->first;
+
+    *outSigningKeyBlob = pair->second;
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
similarity index 88%
rename from identity/aidl/default/IdentityCredential.h
rename to identity/aidl/default/common/IdentityCredential.h
index a8a6409..2281821 100644
--- a/identity/aidl/default/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -29,10 +29,15 @@
 
 #include <cppbor/cppbor.h>
 
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
 namespace aidl::android::hardware::identity {
 
 using ::aidl::android::hardware::keymaster::HardwareAuthToken;
 using ::aidl::android::hardware::keymaster::VerificationToken;
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwarePresentationProxy;
 using ::std::map;
 using ::std::set;
 using ::std::string;
@@ -40,10 +45,11 @@
 
 class IdentityCredential : public BnIdentityCredential {
   public:
-    IdentityCredential(const vector<uint8_t>& credentialData)
-        : credentialData_(credentialData),
+    IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
+                       const vector<uint8_t>& credentialData)
+        : hwProxy_(hwProxy),
+          credentialData_(credentialData),
           numStartRetrievalCalls_(0),
-          authChallenge_(0),
           expectedDeviceNameSpacesSize_(0) {}
 
     // Parses and decrypts credentialData_, return a status code from
@@ -75,14 +81,13 @@
 
   private:
     // Set by constructor
+    sp<SecureHardwarePresentationProxy> hwProxy_;
     vector<uint8_t> credentialData_;
     int numStartRetrievalCalls_;
 
     // Set by initialize()
     string docType_;
     bool testCredential_;
-    vector<uint8_t> storageKey_;
-    vector<uint8_t> credentialPrivKey_;
 
     // Set by createEphemeralKeyPair()
     vector<uint8_t> ephemeralPublicKey_;
@@ -90,9 +95,6 @@
     // Set by setReaderEphemeralPublicKey()
     vector<uint8_t> readerPublicKey_;
 
-    // Set by createAuthChallenge()
-    uint64_t authChallenge_;
-
     // Set by setRequestedNamespaces()
     vector<RequestNamespace> requestNamespaces_;
 
@@ -100,7 +102,6 @@
     VerificationToken verificationToken_;
 
     // Set at startRetrieval() time.
-    map<int32_t, int> profileIdToAccessCheckResult_;
     vector<uint8_t> signingKeyBlob_;
     vector<uint8_t> sessionTranscript_;
     vector<uint8_t> itemsRequest_;
@@ -111,15 +112,16 @@
 
     // Calculated at startRetrieval() time.
     size_t expectedDeviceNameSpacesSize_;
+    vector<unsigned int> expectedNumEntriesPerNamespace_;
 
     // Set at startRetrieveEntryValue() time.
     string currentNameSpace_;
     string currentName_;
+    vector<int32_t> currentAccessControlProfileIds_;
     size_t entryRemainingBytes_;
     vector<uint8_t> entryValue_;
-    vector<uint8_t> entryAdditionalData_;
 
-    size_t calcDeviceNameSpacesSize();
+    void calcDeviceNameSpacesSize(uint32_t accessControlProfileMask);
 };
 
 }  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
similarity index 90%
rename from identity/aidl/default/IdentityCredentialStore.cpp
rename to identity/aidl/default/common/IdentityCredentialStore.cpp
index 30dc6f3..13f91aa 100644
--- a/identity/aidl/default/IdentityCredentialStore.cpp
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -39,8 +39,9 @@
 ndk::ScopedAStatus IdentityCredentialStore::createCredential(
         const string& docType, bool testCredential,
         shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+    sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
     shared_ptr<WritableIdentityCredential> wc =
-            ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
+            ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType, testCredential);
     if (!wc->initialize()) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
@@ -60,8 +61,9 @@
                 "Unsupported cipher suite"));
     }
 
+    sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
     shared_ptr<IdentityCredential> credential =
-            ndk::SharedRefBase::make<IdentityCredential>(credentialData);
+            ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
     auto ret = credential->initialize();
     if (ret != IIdentityCredentialStore::STATUS_OK) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/common/IdentityCredentialStore.h
similarity index 85%
rename from identity/aidl/default/IdentityCredentialStore.h
rename to identity/aidl/default/common/IdentityCredentialStore.h
index 4f3a421..d35e632 100644
--- a/identity/aidl/default/IdentityCredentialStore.h
+++ b/identity/aidl/default/common/IdentityCredentialStore.h
@@ -19,15 +19,20 @@
 
 #include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
 
+#include "SecureHardwareProxy.h"
+
 namespace aidl::android::hardware::identity {
 
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
 using ::std::shared_ptr;
 using ::std::string;
 using ::std::vector;
 
 class IdentityCredentialStore : public BnIdentityCredentialStore {
   public:
-    IdentityCredentialStore() {}
+    IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory)
+        : hwProxyFactory_(hwProxyFactory) {}
 
     // The GCM chunk size used by this implementation is 64 KiB.
     static constexpr size_t kGcmChunkSize = 64 * 1024;
@@ -41,6 +46,9 @@
 
     ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
                                      shared_ptr<IIdentityCredential>* outCredential) override;
+
+  private:
+    sp<SecureHardwareProxyFactory> hwProxyFactory_;
 };
 
 }  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
new file mode 100644
index 0000000..b89ad87
--- /dev/null
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+
+#include <utils/RefBase.h>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::identity {
+
+using ::android::RefBase;
+using ::std::optional;
+using ::std::pair;
+using ::std::string;
+using ::std::vector;
+
+// These classes are used to communicate with Secure Hardware. They mimic the
+// API in libEmbeddedIC 1:1 (except for using C++ types) as each call is intended
+// to be forwarded to the Secure Hardware.
+//
+// Instances are instantiated when a provisioning or presentation session
+// starts. When the session is complete, the shutdown() method is called.
+//
+
+// Forward declare.
+//
+class SecureHardwareProvisioningProxy;
+class SecureHardwarePresentationProxy;
+
+// This is a class used to create proxies.
+//
+class SecureHardwareProxyFactory : public RefBase {
+  public:
+    SecureHardwareProxyFactory() {}
+    virtual ~SecureHardwareProxyFactory() {}
+
+    virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0;
+    virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0;
+};
+
+// The proxy used for provisioning.
+//
+class SecureHardwareProvisioningProxy : public RefBase {
+  public:
+    SecureHardwareProvisioningProxy() {}
+    virtual ~SecureHardwareProvisioningProxy() {}
+
+    virtual bool initialize(bool testCredential) = 0;
+
+    // Returns public key certificate chain with attestation.
+    //
+    // This must return an entire certificate chain and its implementation must
+    // be coordinated with the implementation of eicOpsCreateCredentialKey() on
+    // the TA side (which may return just a single certificate or the entire
+    // chain).
+    virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+                                                          const vector<uint8_t>& applicationId) = 0;
+
+    virtual bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+                                      const string& docType,
+                                      size_t expectedProofOfProvisioningSize) = 0;
+
+    // Returns MAC (28 bytes).
+    virtual optional<vector<uint8_t>> addAccessControlProfile(
+            int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+            uint64_t timeoutMillis, uint64_t secureUserId) = 0;
+
+    virtual bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+                               const string& name, uint64_t entrySize) = 0;
+
+    // Returns encryptedContent.
+    virtual optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+                                                    const string& nameSpace, const string& name,
+                                                    const vector<uint8_t>& content) = 0;
+
+    // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+    virtual optional<vector<uint8_t>> finishAddingEntries() = 0;
+
+    // Returns encryptedCredentialKeys (80 bytes).
+    virtual optional<vector<uint8_t>> finishGetCredentialData(const string& docType) = 0;
+
+    virtual bool shutdown() = 0;
+};
+
+enum AccessCheckResult {
+    kOk,
+    kFailed,
+    kNoAccessControlProfiles,
+    kUserAuthenticationFailed,
+    kReaderAuthenticationFailed,
+};
+
+// The proxy used for presentation.
+//
+class SecureHardwarePresentationProxy : public RefBase {
+  public:
+    SecureHardwarePresentationProxy() {}
+    virtual ~SecureHardwarePresentationProxy() {}
+
+    virtual bool initialize(bool testCredential, string docType,
+                            vector<uint8_t> encryptedCredentialKeys) = 0;
+
+    // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+    virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+                                                                                    time_t now) = 0;
+
+    // Returns private key
+    virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0;
+
+    virtual optional<uint64_t> createAuthChallenge() = 0;
+
+    virtual bool startRetrieveEntries() = 0;
+
+    virtual bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                              int hardwareAuthenticatorType, uint64_t timeStamp,
+                              const vector<uint8_t>& mac, uint64_t verificationTokenChallenge,
+                              uint64_t verificationTokenTimestamp,
+                              int verificationTokenSecurityLevel,
+                              const vector<uint8_t>& verificationTokenMac) = 0;
+
+    virtual bool pushReaderCert(const vector<uint8_t>& certX509) = 0;
+
+    virtual optional<bool> validateAccessControlProfile(int id,
+                                                        const vector<uint8_t>& readerCertificate,
+                                                        bool userAuthenticationRequired,
+                                                        int timeoutMillis, uint64_t secureUserId,
+                                                        const vector<uint8_t>& mac) = 0;
+
+    virtual bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+                                        const vector<uint8_t>& requestMessage, int coseSignAlg,
+                                        const vector<uint8_t>& readerSignatureOfToBeSigned) = 0;
+
+    virtual bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+                            const vector<uint8_t>& readerEphemeralPublicKey,
+                            const vector<uint8_t>& signingKeyBlob, const string& docType,
+                            unsigned int numNamespacesWithValues,
+                            size_t expectedProofOfProvisioningSize) = 0;
+
+    virtual AccessCheckResult startRetrieveEntryValue(
+            const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+            int32_t entrySize, const vector<int32_t>& accessControlProfileIds) = 0;
+
+    virtual optional<vector<uint8_t>> retrieveEntryValue(
+            const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+            const vector<int32_t>& accessControlProfileIds) = 0;
+
+    virtual optional<vector<uint8_t>> finishRetrieval();
+
+    virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
+                                                       size_t proofOfDeletionCborSize) = 0;
+
+    virtual bool shutdown() = 0;
+};
+
+}  // namespace android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
similarity index 70%
rename from identity/aidl/default/WritableIdentityCredential.cpp
rename to identity/aidl/default/common/WritableIdentityCredential.cpp
index 141b4de..1328f36 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "WritableIdentityCredential"
 
 #include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
 
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 
@@ -30,8 +29,8 @@
 #include <utility>
 
 #include "IdentityCredentialStore.h"
-#include "Util.h"
-#include "WritableIdentityCredential.h"
+
+#include "FakeSecureHardwareProxy.h"
 
 namespace aidl::android::hardware::identity {
 
@@ -40,74 +39,55 @@
 using namespace ::android::hardware::identity;
 
 bool WritableIdentityCredential::initialize() {
-    optional<vector<uint8_t>> random = support::getRandom(16);
-    if (!random) {
-        LOG(ERROR) << "Error creating storageKey";
+    if (!hwProxy_->initialize(testCredential_)) {
+        LOG(ERROR) << "hwProxy->initialize failed";
         return false;
     }
-    storageKey_ = random.value();
     startPersonalizationCalled_ = false;
     firstEntry_ = true;
 
     return true;
 }
 
-// This function generates the attestation certificate using the passed in
-// |attestationApplicationId| and |attestationChallenge|.  It will generate an
-// attestation certificate with current time and expires one year from now.  The
-// certificate shall contain all values as specified in hal.
+WritableIdentityCredential::~WritableIdentityCredential() {}
+
 ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
-        const vector<uint8_t>& attestationApplicationId,  //
-        const vector<uint8_t>& attestationChallenge,      //
-        vector<Certificate>* outCertificateChain) {
-    if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
+        const vector<uint8_t>& attestationApplicationId,
+        const vector<uint8_t>& attestationChallenge, vector<Certificate>* outCertificateChain) {
+    if (getAttestationCertificateAlreadyCalled_) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
                 "Error attestation certificate previously generated"));
     }
+    getAttestationCertificateAlreadyCalled_ = true;
+
     if (attestationChallenge.empty()) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
     }
 
-    vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
-    vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
-
-    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
-            support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
-    if (!keyAttestationPair) {
-        LOG(ERROR) << "Error creating credentialKey and attestation";
+    optional<vector<uint8_t>> certChain =
+            hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
+    if (!certChain) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error creating credentialKey and attestation"));
+                "Error generating attestation certificate chain"));
     }
 
-    vector<uint8_t> keyPair = keyAttestationPair.value().first;
-    certificateChain_ = keyAttestationPair.value().second;
-
-    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
-    if (!pubKey) {
+    optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
+    if (!certs) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of credentialKey"));
+                "Error splitting chain into separate certificates"));
     }
-    credentialPubKey_ = pubKey.value();
 
-    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
-    if (!privKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting private part of credentialKey"));
-    }
-    credentialPrivKey_ = privKey.value();
-
-    // convert from vector<vector<uint8_t>>> to vector<Certificate>*
     *outCertificateChain = vector<Certificate>();
-    for (const vector<uint8_t>& cert : certificateChain_) {
+    for (const vector<uint8_t>& cert : certs.value()) {
         Certificate c = Certificate();
         c.encodedCertificate = cert;
         outCertificateChain->push_back(std::move(c));
     }
+
     return ndk::ScopedAStatus::ok();
 }
 
@@ -123,8 +103,8 @@
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
     }
-
     startPersonalizationCalled_ = true;
+
     numAccessControlProfileRemaining_ = accessControlProfileCount;
     remainingEntryCounts_ = entryCounts;
     entryNameSpace_ = "";
@@ -133,6 +113,12 @@
     signedDataNamespaces_ = cppbor::Map();
     signedDataCurrentNamespace_ = cppbor::Array();
 
+    if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
+                                        expectedProofOfProvisioningSize_)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
+    }
+
     return ndk::ScopedAStatus::ok();
 }
 
@@ -140,8 +126,6 @@
         int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
         int64_t timeoutMillis, int64_t secureUserId,
         SecureAccessControlProfile* outSecureAccessControlProfile) {
-    SecureAccessControlProfile profile;
-
     if (numAccessControlProfileRemaining_ == 0) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_INVALID_DATA,
@@ -169,25 +153,21 @@
                 "userAuthenticationRequired is false but timeout is non-zero"));
     }
 
-    // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
-    if (userAuthenticationRequired && secureUserId == 0) {
+    optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
+            id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
+            secureUserId);
+    if (!mac) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "userAuthenticationRequired is true but secureUserId is zero"));
+                IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
     }
 
+    SecureAccessControlProfile profile;
     profile.id = id;
     profile.readerCertificate = readerCertificate;
     profile.userAuthenticationRequired = userAuthenticationRequired;
     profile.timeoutMillis = timeoutMillis;
     profile.secureUserId = secureUserId;
-    optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
-    if (!mac) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
-    }
     profile.mac = mac.value();
-
     cppbor::Map profileMap;
     profileMap.add("id", profile.id);
     if (profile.readerCertificate.encodedCertificate.size() > 0) {
@@ -261,14 +241,18 @@
         remainingEntryCounts_[0] -= 1;
     }
 
-    entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
     entryRemainingBytes_ = entrySize;
     entryNameSpace_ = nameSpace;
     entryName_ = name;
     entryAccessControlProfileIds_ = accessControlProfileIds;
     entryBytes_.resize(0);
     // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+
+    if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
+    }
+
     return ndk::ScopedAStatus::ok();
 }
 
@@ -297,16 +281,11 @@
         }
     }
 
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
-    }
-    optional<vector<uint8_t>> encryptedContent =
-            support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+    optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
+            entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
     if (!encryptedContent) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
+                IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
     }
 
     if (entryRemainingBytes_ == 0) {
@@ -332,50 +311,6 @@
     return ndk::ScopedAStatus::ok();
 }
 
-// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
-// |credentialPrivKey|.
-static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
-                                   const vector<uint8_t>& credentialPrivKey,
-                                   vector<uint8_t>& credentialKeys) {
-    if (storageKey.size() != 16) {
-        LOG(ERROR) << "Size of storageKey is not 16";
-        return false;
-    }
-
-    cppbor::Array array;
-    array.add(cppbor::Bstr(storageKey));
-    array.add(cppbor::Bstr(credentialPrivKey));
-    credentialKeys = array.encode();
-    return true;
-}
-
-// Writes CBOR-encoded structure to |credentialData| containing |docType|,
-// |testCredential| and |credentialKeys|. The latter element will be stored in
-// encrypted form, using |hardwareBoundKey| as the encryption key.
-bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
-                            bool testCredential, const vector<uint8_t>& credentialKeys,
-                            vector<uint8_t>& credentialData) {
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        LOG(ERROR) << "Error getting random";
-        return false;
-    }
-    vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
-    optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
-            hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
-    if (!credentialBlob) {
-        LOG(ERROR) << "Error encrypting CredentialKeys blob";
-        return false;
-    }
-
-    cppbor::Array array;
-    array.add(docType);
-    array.add(testCredential);
-    array.add(cppbor::Bstr(credentialBlob.value()));
-    credentialData = array.encode();
-    return true;
-}
-
 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
         vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
     if (numAccessControlProfileRemaining_ != 0) {
@@ -411,31 +346,37 @@
                         .c_str()));
     }
 
-    optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
-                                                                 encodedCbor,  // payload
-                                                                 {},           // additionalData
-                                                                 {});          // certificateChain
+    optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
+    if (!signatureOfToBeSigned) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
+    }
+
+    optional<vector<uint8_t>> signature =
+            support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+                                                encodedCbor,  // data
+                                                {});          // certificateChain
     if (!signature) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
     }
 
-    vector<uint8_t> credentialKeys;
-    if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
+    optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
+    if (!encryptedCredentialKeys) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error generating encrypted CredentialKeys"));
     }
-
-    vector<uint8_t> credentialData;
-    if (!generateCredentialData(
-                testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
-                docType_, testCredential_, credentialKeys, credentialData)) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
-    }
+    cppbor::Array array;
+    array.add(docType_);
+    array.add(testCredential_);
+    array.add(encryptedCredentialKeys.value());
+    vector<uint8_t> credentialData = array.encode();
 
     *outCredentialData = credentialData;
     *outProofOfProvisioningSignature = signature.value();
+    hwProxy_->shutdown();
+
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
similarity index 85%
rename from identity/aidl/default/WritableIdentityCredential.h
rename to identity/aidl/default/common/WritableIdentityCredential.h
index 5645852..c6f0628 100644
--- a/identity/aidl/default/WritableIdentityCredential.h
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -23,16 +23,24 @@
 #include <cppbor.h>
 #include <set>
 
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
 namespace aidl::android::hardware::identity {
 
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProvisioningProxy;
 using ::std::set;
 using ::std::string;
 using ::std::vector;
 
 class WritableIdentityCredential : public BnWritableIdentityCredential {
   public:
-    WritableIdentityCredential(const string& docType, bool testCredential)
-        : docType_(docType), testCredential_(testCredential) {}
+    WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
+                               bool testCredential)
+        : hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
+
+    ~WritableIdentityCredential();
 
     // Creates the Credential Key. Returns false on failure. Must be called
     // right after construction.
@@ -57,7 +65,6 @@
     ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
                                      const string& nameSpace, const string& name,
                                      int32_t entrySize) override;
-
     ndk::ScopedAStatus addEntryValue(const vector<uint8_t>& content,
                                      vector<uint8_t>* outEncryptedContent) override;
 
@@ -66,18 +73,17 @@
             vector<uint8_t>* outProofOfProvisioningSignature) override;
 
   private:
+    // Set by constructor.
+    sp<SecureHardwareProvisioningProxy> hwProxy_;
     string docType_;
     bool testCredential_;
 
     // This is set in initialize().
-    vector<uint8_t> storageKey_;
     bool startPersonalizationCalled_;
     bool firstEntry_;
 
-    // These are set in getAttestationCertificate().
-    vector<uint8_t> credentialPrivKey_;
-    vector<uint8_t> credentialPubKey_;
-    vector<vector<uint8_t>> certificateChain_;
+    // This is set in getAttestationCertificate().
+    bool getAttestationCertificateAlreadyCalled_ = false;
 
     // These fields are initialized during startPersonalization()
     size_t numAccessControlProfileRemaining_;
@@ -92,7 +98,6 @@
 
     // These fields are initialized during beginAddEntry()
     size_t entryRemainingBytes_;
-    vector<uint8_t> entryAdditionalData_;
     string entryNameSpace_;
     string entryName_;
     vector<int32_t> entryAccessControlProfileIds_;
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
new file mode 100644
index 0000000..ec049b1
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EicCbor.h"
+
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+    cbor->size = 0;
+    cbor->bufferSize = bufferSize;
+    cbor->buffer = buffer;
+    cbor->digestType = EIC_CBOR_DIGEST_TYPE_SHA256;
+    eicOpsSha256Init(&cbor->digester.sha256);
+}
+
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+                           const uint8_t* hmacKey, size_t hmacKeySize) {
+    cbor->size = 0;
+    cbor->bufferSize = bufferSize;
+    cbor->buffer = buffer;
+    cbor->digestType = EIC_CBOR_DIGEST_TYPE_HMAC_SHA256;
+    eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
+}
+
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    switch (cbor->digestType) {
+        case EIC_CBOR_DIGEST_TYPE_SHA256:
+            eicOpsSha256Final(&cbor->digester.sha256, digest);
+            break;
+        case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+            eicOpsHmacSha256Final(&cbor->digester.hmacSha256, digest);
+            break;
+    }
+}
+
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size) {
+    switch (cbor->digestType) {
+        case EIC_CBOR_DIGEST_TYPE_SHA256:
+            eicOpsSha256Update(&cbor->digester.sha256, data, size);
+            break;
+        case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+            eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
+            break;
+    }
+
+    if (cbor->size >= cbor->bufferSize) {
+        cbor->size += size;
+        return;
+    }
+
+    size_t numBytesLeft = cbor->bufferSize - cbor->size;
+    size_t numBytesToCopy = size;
+    if (numBytesToCopy > numBytesLeft) {
+        numBytesToCopy = numBytesLeft;
+    }
+    eicMemCpy(cbor->buffer + cbor->size, data, numBytesToCopy);
+
+    cbor->size += size;
+}
+
+size_t eicCborAdditionalLengthBytesFor(size_t size) {
+    if (size < 24) {
+        return 0;
+    } else if (size <= 0xff) {
+        return 1;
+    } else if (size <= 0xffff) {
+        return 2;
+    } else if (size <= 0xffffffff) {
+        return 4;
+    }
+    return 8;
+}
+
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size) {
+    uint8_t data[9];
+
+    if (size < 24) {
+        data[0] = (majorType << 5) | size;
+        eicCborAppend(cbor, data, 1);
+    } else if (size <= 0xff) {
+        data[0] = (majorType << 5) | 24;
+        data[1] = size;
+        eicCborAppend(cbor, data, 2);
+    } else if (size <= 0xffff) {
+        data[0] = (majorType << 5) | 25;
+        data[1] = size >> 8;
+        data[2] = size & 0xff;
+        eicCborAppend(cbor, data, 3);
+    } else if (size <= 0xffffffff) {
+        data[0] = (majorType << 5) | 26;
+        data[1] = (size >> 24) & 0xff;
+        data[2] = (size >> 16) & 0xff;
+        data[3] = (size >> 8) & 0xff;
+        data[4] = size & 0xff;
+        eicCborAppend(cbor, data, 5);
+    } else {
+        data[0] = (majorType << 5) | 24;
+        data[1] = (((uint64_t)size) >> 56) & 0xff;
+        data[2] = (((uint64_t)size) >> 48) & 0xff;
+        data[3] = (((uint64_t)size) >> 40) & 0xff;
+        data[4] = (((uint64_t)size) >> 32) & 0xff;
+        data[5] = (((uint64_t)size) >> 24) & 0xff;
+        data[6] = (((uint64_t)size) >> 16) & 0xff;
+        data[7] = (((uint64_t)size) >> 8) & 0xff;
+        data[8] = ((uint64_t)size) & 0xff;
+        eicCborAppend(cbor, data, 9);
+    }
+}
+
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dataSize);
+    eicCborAppend(cbor, data, dataSize);
+}
+
+void eicCborAppendString(EicCbor* cbor, const char* str) {
+    size_t length = eicStrLen(str);
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_STRING, length);
+    eicCborAppend(cbor, (const uint8_t*)str, length);
+}
+
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SIMPLE, simpleValue);
+}
+
+void eicCborAppendBool(EicCbor* cbor, bool value) {
+    uint8_t simpleValue = value ? EIC_CBOR_SIMPLE_VALUE_TRUE : EIC_CBOR_SIMPLE_VALUE_FALSE;
+    eicCborAppendSimple(cbor, simpleValue);
+}
+
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value) {
+    size_t encoded = value;
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SEMANTIC, encoded);
+}
+
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value) {
+    size_t encoded = value;
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_UNSIGNED, encoded);
+}
+
+void eicCborAppendNumber(EicCbor* cbor, int64_t value) {
+    if (value < 0) {
+        size_t encoded = -1 - value;
+        eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_NEGATIVE, encoded);
+    } else {
+        eicCborAppendUnsigned(cbor, value);
+    }
+}
+
+void eicCborAppendArray(EicCbor* cbor, size_t numElements) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, numElements);
+}
+
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_MAP, numPairs);
+}
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+                              size_t readerCertificateSize, bool userAuthenticationRequired,
+                              uint64_t timeoutMillis, uint64_t secureUserId) {
+    size_t numPairs = 1;
+    if (readerCertificateSize > 0) {
+        numPairs += 1;
+    }
+    if (userAuthenticationRequired) {
+        numPairs += 2;
+        if (secureUserId > 0) {
+            numPairs += 1;
+        }
+    }
+    eicCborAppendMap(cborBuilder, numPairs);
+    eicCborAppendString(cborBuilder, "id");
+    eicCborAppendUnsigned(cborBuilder, id);
+    if (readerCertificateSize > 0) {
+        eicCborAppendString(cborBuilder, "readerCertificate");
+        eicCborAppendByteString(cborBuilder, readerCertificate, readerCertificateSize);
+    }
+    if (userAuthenticationRequired) {
+        eicCborAppendString(cborBuilder, "userAuthenticationRequired");
+        eicCborAppendBool(cborBuilder, userAuthenticationRequired);
+        eicCborAppendString(cborBuilder, "timeoutMillis");
+        eicCborAppendUnsigned(cborBuilder, timeoutMillis);
+        if (secureUserId > 0) {
+            eicCborAppendString(cborBuilder, "secureUserId");
+            eicCborAppendUnsigned(cborBuilder, secureUserId);
+        }
+    }
+
+    if (cborBuilder->size > cborBuilder->bufferSize) {
+        eicDebug("Buffer for ACP CBOR is too small (%zd) - need %zd bytes", cborBuilder->bufferSize,
+                 cborBuilder->size);
+        return false;
+    }
+
+    return true;
+}
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+                                    size_t numAccessControlProfileIds, const char* nameSpace,
+                                    const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+                                    size_t* outAdditionalDataCborSize,
+                                    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]) {
+    EicCbor cborBuilder;
+
+    eicCborInit(&cborBuilder, cborBuffer, cborBufferSize);
+    eicCborAppendMap(&cborBuilder, 3);
+    eicCborAppendString(&cborBuilder, "Namespace");
+    eicCborAppendString(&cborBuilder, nameSpace);
+    eicCborAppendString(&cborBuilder, "Name");
+    eicCborAppendString(&cborBuilder, name);
+    eicCborAppendString(&cborBuilder, "AccessControlProfileIds");
+    eicCborAppendArray(&cborBuilder, numAccessControlProfileIds);
+    for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+        eicCborAppendNumber(&cborBuilder, accessControlProfileIds[n]);
+    }
+    if (cborBuilder.size > cborBufferSize) {
+        eicDebug("Not enough space for additionalData - buffer is only %zd bytes, content is %zd",
+                 cborBufferSize, cborBuilder.size);
+        return false;
+    }
+    if (outAdditionalDataCborSize != NULL) {
+        *outAdditionalDataCborSize = cborBuilder.size;
+    }
+    eicCborFinal(&cborBuilder, additionalDataSha256);
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
new file mode 100644
index 0000000..4686b38
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicOps.h"
+
+typedef enum {
+    EIC_CBOR_DIGEST_TYPE_SHA256,
+    EIC_CBOR_DIGEST_TYPE_HMAC_SHA256,
+} EicCborDigestType;
+
+/* EicCbor is a utility class to build CBOR data structures and calculate
+ * digests on the fly.
+ */
+typedef struct {
+    // Contains the size of the built CBOR, even if it exceeds bufferSize (will
+    // never write to buffer beyond bufferSize though)
+    size_t size;
+
+    // The size of the buffer. Is zero if no data is recorded in which case
+    // only digesting is performed.
+    size_t bufferSize;
+
+    // Whether we're producing a SHA-256 or HMAC-SHA256 digest.
+    EicCborDigestType digestType;
+
+    // The SHA-256 digester object.
+    union {
+        EicSha256Ctx sha256;
+        EicHmacSha256Ctx hmacSha256;
+    } digester;
+
+    // The buffer used for building up CBOR or NULL if bufferSize is 0.
+    uint8_t* buffer;
+} EicCbor;
+
+/* Initializes an EicCbor.
+ *
+ * The given buffer will be used, up to bufferSize.
+ *
+ * If bufferSize is 0, buffer may be NULL.
+ */
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize);
+
+/* Like eicCborInit() but uses HMAC-SHA256 instead of SHA-256.
+ */
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+                           const uint8_t* hmacKey, size_t hmacKeySize);
+
+/* Finishes building CBOR and returns the digest. */
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+/* Appends CBOR data to the EicCbor. */
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size);
+
+#define EIC_CBOR_MAJOR_TYPE_UNSIGNED 0
+#define EIC_CBOR_MAJOR_TYPE_NEGATIVE 1
+#define EIC_CBOR_MAJOR_TYPE_BYTE_STRING 2
+#define EIC_CBOR_MAJOR_TYPE_STRING 3
+#define EIC_CBOR_MAJOR_TYPE_ARRAY 4
+#define EIC_CBOR_MAJOR_TYPE_MAP 5
+#define EIC_CBOR_MAJOR_TYPE_SEMANTIC 6
+#define EIC_CBOR_MAJOR_TYPE_SIMPLE 7
+
+#define EIC_CBOR_SIMPLE_VALUE_FALSE 20
+#define EIC_CBOR_SIMPLE_VALUE_TRUE 21
+
+#define EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR 24
+
+/* Begins a new CBOR value. */
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size);
+
+/* Appends a bytestring. */
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize);
+
+/* Appends a NUL-terminated UTF-8 string. */
+void eicCborAppendString(EicCbor* cbor, const char* str);
+
+/* Appends a simple value. */
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue);
+
+/* Appends a boolean. */
+void eicCborAppendBool(EicCbor* cbor, bool value);
+
+/* Appends a semantic */
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value);
+
+/* Appends an unsigned number. */
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value);
+
+/* Appends a number. */
+void eicCborAppendNumber(EicCbor* cbor, int64_t value);
+
+/* Starts appending an array.
+ *
+ * After this numElements CBOR elements must follow.
+ */
+void eicCborAppendArray(EicCbor* cbor, size_t numElements);
+
+/* Starts appending a map.
+ *
+ * After this numPairs pairs of CBOR elements must follow.
+ */
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs);
+
+/* Calculates how many bytes are needed to store a size. */
+size_t eicCborAdditionalLengthBytesFor(size_t size);
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+                              size_t readerCertificateSize, bool userAuthenticationRequired,
+                              uint64_t timeoutMillis, uint64_t secureUserId);
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+                                    size_t numAccessControlProfileIds, const char* nameSpace,
+                                    const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+                                    size_t* outAdditionalDataCborSize,
+                                    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]);
+
+// The maximum size of an encoded Secure Access Control Profile that we
+// support. Since the SACP may contain a reader certificate chain these can get
+// pretty big.
+//
+// Currently we allocate space on the stack for this structure which is why we
+// have a maximum size. We can get rid of the maximum size by incrementally
+// building/verifying the SACP. TODO: actually do this.
+//
+#define EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE 512
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
new file mode 100644
index 0000000..da4dabf
--- /dev/null
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Uncomment or define if debug messages are needed.
+//
+//#define EIC_DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The following defines must be set to something appropriate
+//
+//   EIC_SHA256_CONTEXT_SIZE - the size of EicSha256Ctx
+//   EIC_HMAC_SHA256_CONTEXT_SIZE - the size of EicHmacSha256Ctx
+//
+// For example, if EicSha256Ctx is implemented using BoringSSL this would be defined
+// as sizeof(SHA256_CTX).
+//
+// We expect the implementation to provide a header file with the name
+// EicOpsImpl.h to do all this.
+//
+#include "EicOpsImpl.h"
+
+#define EIC_SHA256_DIGEST_SIZE 32
+
+// The size of a P-256 private key.
+//
+#define EIC_P256_PRIV_KEY_SIZE 32
+
+// The size of a P-256 public key in uncompressed form.
+//
+// The public key is stored in uncompressed form, first the X coordinate, then
+// the Y coordinate.
+//
+#define EIC_P256_PUB_KEY_SIZE 64
+
+// Size of one of the coordinates in a curve-point.
+//
+#define EIC_P256_COORDINATE_SIZE 32
+
+// The size of an ECSDA signature using P-256.
+//
+// The R and S values are stored here, first R then S.
+//
+#define EIC_ECDSA_P256_SIGNATURE_SIZE 64
+
+#define EIC_AES_128_KEY_SIZE 16
+
+// The following are definitions of implementation functions the
+// underlying platform must provide.
+//
+
+struct EicSha256Ctx {
+    uint8_t reserved[EIC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicSha256Ctx EicSha256Ctx;
+
+struct EicHmacSha256Ctx {
+    uint8_t reserved[EIC_HMAC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicHmacSha256Ctx EicHmacSha256Ctx;
+
+#ifdef EIC_DEBUG
+// Debug macro. Don't include a new-line in message.
+//
+#define eicDebug(...)                            \
+    do {                                         \
+        eicPrint("%s:%d: ", __FILE__, __LINE__); \
+        eicPrint(__VA_ARGS__);                   \
+        eicPrint("\n");                          \
+    } while (0)
+#else
+#define eicDebug(...) \
+    do {              \
+    } while (0)
+#endif
+
+// Prints message which should include new-line character. Can be no-op.
+//
+// Don't use this from code, use eicDebug() instead.
+//
+#ifdef EIC_DEBUG
+void eicPrint(const char* format, ...);
+#else
+inline void eicPrint(const char*, ...) {}
+#endif
+
+// Dumps data as pretty-printed hex. Can be no-op.
+//
+#ifdef EIC_DEBUG
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize);
+#else
+inline void eicHexdump(const char*, const uint8_t*, size_t) {}
+#endif
+
+// Pretty-prints encoded CBOR. Can be no-op.
+//
+// If a byte-string is larger than |maxBStrSize| its contents will not be
+// printed, instead the value of the form "<bstr size=1099016
+// sha1=ef549cca331f73dfae2090e6a37c04c23f84b07b>" will be printed. Pass zero
+// for |maxBStrSize| to disable this.
+//
+#ifdef EIC_DEBUG
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize);
+#else
+inline void eicCborPrettyPrint(const uint8_t*, size_t, size_t) {}
+#endif
+
+// Memory setting, see memset(3).
+void* eicMemSet(void* s, int c, size_t n);
+
+// Memory copying, see memcpy(3).
+void* eicMemCpy(void* dest, const void* src, size_t n);
+
+// String length, see strlen(3).
+size_t eicStrLen(const char* s);
+
+// Memory compare, see CRYPTO_memcmp(3SSL)
+//
+// It takes an amount of time dependent on len, but independent of the contents of the
+// memory regions pointed to by s1 and s2.
+//
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n);
+
+// Random number generation.
+bool eicOpsRandom(uint8_t* buf, size_t numBytes);
+
+// If |testCredential| is true, returns the 128-bit AES Hardware-Bound Key (16 bytes).
+//
+// Otherwise returns all zeroes (16 bytes).
+//
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential);
+
+// Encrypts |data| with |key| and |additionalAuthenticatedData| using |nonce|,
+// returns the resulting (nonce || ciphertext || tag) in |encryptedData| which
+// must be of size |dataSize| + 28.
+bool eicOpsEncryptAes128Gcm(
+        const uint8_t* key,    // Must be 16 bytes
+        const uint8_t* nonce,  // Must be 12 bytes
+        const uint8_t* data,   // May be NULL if size is 0
+        size_t dataSize,
+        const uint8_t* additionalAuthenticationData,  // May be NULL if size is 0
+        size_t additionalAuthenticationDataSize, uint8_t* encryptedData);
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key,  // Must be 16 bytes
+                            const uint8_t* encryptedData, size_t encryptedDataSize,
+                            const uint8_t* additionalAuthenticationData,
+                            size_t additionalAuthenticationDataSize, uint8_t* data);
+
+// Creates an EC key using the P-256 curve. The private key is written to
+// |privateKey|. The public key is written to |publicKey|.
+//
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                       uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]);
+
+// Generates CredentialKey plus an attestation certificate.
+//
+// The attestation certificate will be signed by the attestation keys the secure
+// area has been provisioned with. The given |challenge| and |applicationId|
+// will be used as will |testCredential|.
+//
+// The generated certificate will be in X.509 format and returned in |cert|
+// and |certSize| must be set to the size of this array and this function will
+// set it to the size of the certification chain on successfully return.
+//
+// This may return either a single certificate or an entire certificate
+// chain. If it returns only a single certificate, the implementation of
+// SecureHardwareProvisioningProxy::createCredentialKey() should amend the
+// remainder of the certificate chain on the HAL side.
+//
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+                               size_t challengeSize, const uint8_t* applicationId,
+                               size_t applicationIdSize, bool testCredential, uint8_t* cert,
+                               size_t* certSize);  // inout
+
+// Generate an X.509 certificate for the key identified by |publicKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+// The certificate will be signed by the key identified by |signingKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                     const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+                     const char* issuerName, const char* subjectName, time_t validityNotBefore,
+                     time_t validityNotAfter, uint8_t* cert,
+                     size_t* certSize);  // inout
+
+// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
+// be given by |digestOfData|). Returns the signature in |signature|.
+//
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                 const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+                 uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// Performs Elliptic Curve Diffie-Helman.
+//
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]);
+
+// Performs HKDF.
+//
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+                size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+                size_t outputSize);
+
+// SHA-256 functions.
+void eicOpsSha256Init(EicSha256Ctx* ctx);
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// HMAC SHA-256 functions.
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize);
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// Extracts the public key in the given X.509 certificate.
+//
+// If the key is not an EC key, this function fails.
+//
+// Otherwise the public key is stored in uncompressed form in |publicKey| which
+// size should be set in |publicKeySize|. On successful return |publicKeySize|
+// is set to the length of the key. If there is not enough space, the function
+// fails.
+//
+// (The public key returned is not necessarily a P-256 key, even if it is note
+// that its size is not EIC_P256_PUBLIC_KEY_SIZE because of the leading 0x04.)
+//
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+                            size_t* publicKeySize);
+
+// Checks that the X.509 certificate given by |x509Cert| is signed by the public
+// key given by |publicKey| which must be an EC key in uncompressed form (e.g.
+// same formatt as returned by eicOpsX509GetPublicKey()).
+//
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+                                     const uint8_t* publicKey, size_t publicKeySize);
+
+// Checks that |signature| is a signature of some data (given by |digest|),
+// signed by the public key given by |publicKey|.
+//
+// The key must be an EC key in uncompressed form (e.g.  same format as returned
+// by eicOpsX509GetPublicKey()).
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// The size of digest must match the size of the key.
+//
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+                                    const uint8_t* signature, size_t signatureSize,
+                                    const uint8_t* publicKey, size_t publicKeySize);
+
+// Validates that the passed in data constitutes a valid auth- and verification tokens.
+//
+bool eicOpsValidateAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                             int hardwareAuthenticatorType, uint64_t timeStamp, const uint8_t* mac,
+                             size_t macSize, uint64_t verificationTokenChallenge,
+                             uint64_t verificationTokenTimeStamp,
+                             int verificationTokenSecurityLevel,
+                             const uint8_t* verificationTokenMac, size_t verificationTokenMacSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
new file mode 100644
index 0000000..d3f5556
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EicPresentation.h"
+
+#include <inttypes.h>
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+                         const uint8_t encryptedCredentialKeys[80]) {
+    uint8_t credentialKeys[52];
+
+    eicMemSet(ctx, '\0', sizeof(EicPresentation));
+
+    if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+                                80,
+                                // DocType is the additionalAuthenticatedData
+                                (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+        eicDebug("Error decrypting CredentialKeys");
+        return false;
+    }
+
+    // It's supposed to look like this;
+    //
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr    ; credentialPrivKey, the private key for credentialKey
+    //         ]
+    //
+    // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
+    //
+    // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
+    // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
+    // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
+    //
+    if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
+        credentialKeys[19] != 0x20) {
+        eicDebug("Invalid CBOR for CredentialKeys");
+        return false;
+    }
+    eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+    eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+    ctx->testCredential = testCredential;
+    return true;
+}
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+                                           uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+                                           uint8_t signingKeyBlob[60]) {
+    uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+    uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+
+    if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
+        eicDebug("Error creating signing key");
+        return false;
+    }
+
+    const int secondsInOneYear = 365 * 24 * 60 * 60;
+    time_t validityNotBefore = now;
+    time_t validityNotAfter = now + secondsInOneYear;  // One year from now.
+    if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
+                         "Android Identity Credential Key",                 // issuer CN
+                         "Android Identity Credential Authentication Key",  // subject CN
+                         validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
+        eicDebug("Error creating certificate for signing key");
+        return false;
+    }
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        eicDebug("Error getting random");
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv),
+                                // DocType is the additionalAuthenticatedData
+                                (const uint8_t*)docType, eicStrLen(docType), signingKeyBlob)) {
+        eicDebug("Error encrypting signing key");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+                                           uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) {
+    uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE];
+    if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ephemeralPublicKey)) {
+        eicDebug("Error creating ephemeral key");
+        return false;
+    }
+    eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+    return true;
+}
+
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge) {
+    do {
+        if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(uint64_t))) {
+            eicDebug("Failed generating random challenge");
+            return false;
+        }
+    } while (ctx->authChallenge == 0);
+    eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge);
+    *authChallenge = ctx->authChallenge;
+    return true;
+}
+
+// From "COSE Algorithms" registry
+//
+#define COSE_ALG_ECDSA_256 -7
+
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                                           size_t sessionTranscriptSize,
+                                           const uint8_t* requestMessage, size_t requestMessageSize,
+                                           int coseSignAlg,
+                                           const uint8_t* readerSignatureOfToBeSigned,
+                                           size_t readerSignatureOfToBeSignedSize) {
+    if (ctx->readerPublicKeySize == 0) {
+        eicDebug("No public key for reader");
+        return false;
+    }
+
+    // Right now we only support ECDSA with SHA-256 (e.g. ES256).
+    //
+    if (coseSignAlg != COSE_ALG_ECDSA_256) {
+        eicDebug(
+                "COSE Signature algorithm for reader signature is %d, "
+                "only ECDSA with SHA-256 is supported right now",
+                coseSignAlg);
+        return false;
+    }
+
+    // What we're going to verify is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    //   Sig_structure = [
+    //     context : "Signature" / "Signature1" / "CounterSignature",
+    //     body_protected : empty_or_serialized_map,
+    //     ? sign_protected : empty_or_serialized_map,
+    //     external_aad : bstr,
+    //     payload : bstr
+    //   ]
+    //
+    // So we're going to build that CBOR...
+    //
+    EicCbor cbor;
+    eicCborInit(&cbor, NULL, 0);
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> coseSignAlg (e.g. -7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // External_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time... the CBOR to be written is
+    //
+    //   ReaderAuthentication = [
+    //      "ReaderAuthentication",
+    //      SessionTranscript,
+    //      ItemsRequestBytes
+    //   ]
+    //
+    //   ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+    //
+    //   ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+    //
+    // which is easily calculated below
+    //
+    size_t calculatedSize = 0;
+    calculatedSize += 1;  // Array of size 3
+    calculatedSize += 1;  // "ReaderAuthentication" less than 24 bytes
+    calculatedSize += sizeof("ReaderAuthentication") - 1;  // Don't include trailing NUL
+    calculatedSize += sessionTranscriptSize;               // Already CBOR encoded
+    calculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(requestMessageSize);
+    calculatedSize += requestMessageSize;
+
+    // However note that we're authenticating ReaderAuthenticationBytes which
+    // is a tagged bstr of the bytes of ReaderAuthentication. So need to get
+    // that in front.
+    size_t rabCalculatedSize = 0;
+    rabCalculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    rabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+    rabCalculatedSize += calculatedSize;
+
+    // Begin the bytestring for ReaderAuthenticationBytes;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, rabCalculatedSize);
+
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+    // Begins the bytestring for ReaderAuthentication;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+    // And now that we know the size, let's fill it in...
+    //
+    size_t payloadOffset = cbor.size;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3);
+    eicCborAppendString(&cbor, "ReaderAuthentication");
+    eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize);
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize);
+    eicCborAppend(&cbor, requestMessage, requestMessageSize);
+
+    if (cbor.size != payloadOffset + calculatedSize) {
+        eicDebug("CBOR size is %zd but we expected %zd", cbor.size, payloadOffset + calculatedSize);
+        return false;
+    }
+    uint8_t toBeSignedDigest[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, toBeSignedDigest);
+
+    if (!eicOpsEcDsaVerifyWithPublicKey(
+                toBeSignedDigest, EIC_SHA256_DIGEST_SIZE, readerSignatureOfToBeSigned,
+                readerSignatureOfToBeSignedSize, ctx->readerPublicKey, ctx->readerPublicKeySize)) {
+        eicDebug("Request message is not signed by public key");
+        return false;
+    }
+    ctx->requestMessageValidated = true;
+    return true;
+}
+
+// Validates the next certificate in the reader certificate chain.
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+                                   size_t certX509Size) {
+    // If we had a previous certificate, use its public key to validate this certificate.
+    if (ctx->readerPublicKeySize > 0) {
+        if (!eicOpsX509CertSignedByPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+                                             ctx->readerPublicKeySize)) {
+            eicDebug("Certificate is not signed by public key in the previous certificate");
+            return false;
+        }
+    }
+
+    // Store the key of this certificate, this is used to validate the next certificate
+    // and also ACPs with certificates that use the same public key...
+    ctx->readerPublicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+    if (!eicOpsX509GetPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+                                &ctx->readerPublicKeySize)) {
+        eicDebug("Error extracting public key from certificate");
+        return false;
+    }
+    if (ctx->readerPublicKeySize == 0) {
+        eicDebug("Zero-length public key in certificate");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+                                 uint64_t authenticatorId, int hardwareAuthenticatorType,
+                                 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+                                 uint64_t verificationTokenChallenge,
+                                 uint64_t verificationTokenTimestamp,
+                                 int verificationTokenSecurityLevel,
+                                 const uint8_t* verificationTokenMac,
+                                 size_t verificationTokenMacSize) {
+    if (!eicOpsValidateAuthToken(
+                challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac,
+                macSize, verificationTokenChallenge, verificationTokenTimestamp,
+                verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) {
+        return false;
+    }
+    ctx->authTokenChallenge = challenge;
+    ctx->authTokenSecureUserId = secureUserId;
+    ctx->authTokenTimestamp = timeStamp;
+    ctx->verificationTokenTimestamp = verificationTokenTimestamp;
+    return true;
+}
+
+static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired, int timeoutMillis,
+                          uint64_t secureUserId) {
+    if (!userAuthenticationRequired) {
+        return true;
+    }
+
+    if (secureUserId != ctx->authTokenSecureUserId) {
+        eicDebug("secureUserId in profile differs from userId in authToken");
+        return false;
+    }
+
+    if (timeoutMillis == 0) {
+        if (ctx->authTokenChallenge == 0) {
+            eicDebug("No challenge in authToken");
+            return false;
+        }
+
+        // If we didn't create a challenge, too bad but user auth with
+        // timeoutMillis set to 0 needs it.
+        if (ctx->authChallenge == 0) {
+            eicDebug("No challenge was created for this session");
+            return false;
+        }
+        if (ctx->authTokenChallenge != ctx->authChallenge) {
+            eicDebug("Challenge in authToken (%" PRIu64
+                     ") doesn't match the challenge "
+                     "that was created (%" PRIu64 ") for this session",
+                     ctx->authTokenChallenge, ctx->authChallenge);
+            return false;
+        }
+    }
+
+    uint64_t now = ctx->verificationTokenTimestamp;
+    if (ctx->authTokenTimestamp > now) {
+        eicDebug("Timestamp in authToken is in the future");
+        return false;
+    }
+
+    if (timeoutMillis > 0) {
+        if (now > ctx->authTokenTimestamp + timeoutMillis) {
+            eicDebug("Deadline for authToken is in the past");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool checkReaderAuth(EicPresentation* ctx, const uint8_t* readerCertificate,
+                            size_t readerCertificateSize) {
+    uint8_t publicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+    size_t publicKeySize;
+
+    if (readerCertificateSize == 0) {
+        return true;
+    }
+
+    // Remember in this case certificate equality is done by comparing public
+    // keys, not bitwise comparison of the certificates.
+    //
+    publicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+    if (!eicOpsX509GetPublicKey(readerCertificate, readerCertificateSize, publicKey,
+                                &publicKeySize)) {
+        eicDebug("Error extracting public key from certificate");
+        return false;
+    }
+    if (publicKeySize == 0) {
+        eicDebug("Zero-length public key in certificate");
+        return false;
+    }
+
+    if ((ctx->readerPublicKeySize != publicKeySize) ||
+        (eicCryptoMemCmp(ctx->readerPublicKey, publicKey, ctx->readerPublicKeySize) != 0)) {
+        return false;
+    }
+    return true;
+}
+
+// Note: This function returns false _only_ if an error occurred check for access, _not_
+// whether access is granted. Whether access is granted is returned in |accessGranted|.
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+                                                 const uint8_t* readerCertificate,
+                                                 size_t readerCertificateSize,
+                                                 bool userAuthenticationRequired, int timeoutMillis,
+                                                 uint64_t secureUserId, const uint8_t mac[28],
+                                                 bool* accessGranted) {
+    *accessGranted = false;
+
+    if (id < 0 || id >= 32) {
+        eicDebug("id value of %d is out of allowed range [0, 32[", id);
+        return false;
+    }
+
+    // Validate the MAC
+    uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+    EicCbor cborBuilder;
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis, secureUserId)) {
+        return false;
+    }
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, mac, 28, cborBuilder.buffer, cborBuilder.size,
+                                NULL)) {
+        eicDebug("MAC for AccessControlProfile doesn't match");
+        return false;
+    }
+
+    bool passedUserAuth =
+            checkUserAuth(ctx, userAuthenticationRequired, timeoutMillis, secureUserId);
+    bool passedReaderAuth = checkReaderAuth(ctx, readerCertificate, readerCertificateSize);
+
+    ctx->accessControlProfileMaskValidated |= (1 << id);
+    if (readerCertificateSize > 0) {
+        ctx->accessControlProfileMaskUsesReaderAuth |= (1 << id);
+    }
+    if (!passedReaderAuth) {
+        ctx->accessControlProfileMaskFailedReaderAuth |= (1 << id);
+    }
+    if (!passedUserAuth) {
+        ctx->accessControlProfileMaskFailedUserAuth |= (1 << id);
+    }
+
+    if (passedUserAuth && passedReaderAuth) {
+        *accessGranted = true;
+        eicDebug("Access granted for id %d", id);
+    }
+    return true;
+}
+
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                               size_t sessionTranscriptSize,
+                               const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+                               const uint8_t signingKeyBlob[60], const char* docType,
+                               unsigned int numNamespacesWithValues,
+                               size_t expectedDeviceNamespacesSize) {
+    uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
+                                eicStrLen(docType), signingKeyPriv)) {
+        eicDebug("Error decrypting signingKeyBlob");
+        return false;
+    }
+
+    uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
+    if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
+        eicDebug("ECDH failed");
+        return false;
+    }
+
+    EicCbor cbor;
+    eicCborInit(&cbor, NULL, 0);
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
+    uint8_t salt[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, salt);
+
+    const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+    uint8_t derivedKey[32];
+    if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt), info, sizeof(info),
+                    derivedKey, sizeof(derivedKey))) {
+        eicDebug("HKDF failed");
+        return false;
+    }
+
+    eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
+    ctx->buildCbor = true;
+
+    // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
+    // structure which looks like the following:
+    //
+    // MAC_structure = [
+    //   context : "MAC" / "MAC0",
+    //   protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    // ]
+    //
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "MAC0");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
+    eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time... the CBOR to be written is
+    //
+    //   DeviceAuthentication = [
+    //      "DeviceAuthentication",
+    //      SessionTranscript,
+    //      DocType,                ; DocType as used in Documents structure in OfflineResponse
+    //      DeviceNameSpacesBytes
+    //   ]
+    //
+    //   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+    //
+    //   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+    //
+    // which is easily calculated below
+    //
+    size_t calculatedSize = 0;
+    calculatedSize += 1;  // Array of size 4
+    calculatedSize += 1;  // "DeviceAuthentication" less than 24 bytes
+    calculatedSize += sizeof("DeviceAuthentication") - 1;  // Don't include trailing NUL
+    calculatedSize += sessionTranscriptSize;               // Already CBOR encoded
+    size_t docTypeLen = eicStrLen(docType);
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLen) + docTypeLen;
+    calculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
+    calculatedSize += expectedDeviceNamespacesSize;
+
+    // However note that we're authenticating DeviceAuthenticationBytes which
+    // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
+    // that in front.
+    size_t dabCalculatedSize = 0;
+    dabCalculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+    dabCalculatedSize += calculatedSize;
+
+    // Begin the bytestring for DeviceAuthenticationBytes;
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
+
+    eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+    // Begins the bytestring for DeviceAuthentication;
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "DeviceAuthentication");
+    eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
+    eicCborAppendString(&ctx->cbor, docType);
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedDeviceNamespacesSize);
+    ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
+
+    eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
+    return true;
+}
+
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
+    // HAL may use this object multiple times to retrieve data so need to reset various
+    // state objects here.
+    ctx->requestMessageValidated = false;
+    ctx->buildCbor = false;
+    ctx->accessControlProfileMaskValidated = 0;
+    ctx->accessControlProfileMaskUsesReaderAuth = 0;
+    ctx->accessControlProfileMaskFailedReaderAuth = 0;
+    ctx->accessControlProfileMaskFailedUserAuth = 0;
+    ctx->readerPublicKeySize = 0;
+    return true;
+}
+
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+        EicPresentation* ctx, const char* nameSpace, const char* name,
+        unsigned int newNamespaceNumEntries, int32_t /* entrySize */,
+        const int* accessControlProfileIds, size_t numAccessControlProfileIds,
+        uint8_t* scratchSpace, size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    if (newNamespaceNumEntries > 0) {
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
+    }
+
+    // We'll need to calc and store a digest of additionalData to check that it's the same
+    // additionalData being passed in for every eicPresentationRetrieveEntryValue() call...
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        ctx->additionalDataSha256)) {
+        return EIC_ACCESS_CHECK_RESULT_FAILED;
+    }
+
+    if (numAccessControlProfileIds == 0) {
+        return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
+    }
+
+    // Access is granted if at least one of the profiles grants access.
+    //
+    // If an item is configured without any profiles, access is denied.
+    //
+    EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
+    for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+        int id = accessControlProfileIds[n];
+        uint32_t idBitMask = (1 << id);
+
+        // If the access control profile wasn't validated, this is an error and we
+        // fail immediately.
+        bool validated = ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
+        if (!validated) {
+            eicDebug("No ACP for profile id %d", id);
+            return EIC_ACCESS_CHECK_RESULT_FAILED;
+        }
+
+        // Otherwise, we _did_ validate the profile. If none of the checks
+        // failed, we're done
+        bool failedUserAuth = ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
+        bool failedReaderAuth = ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
+        if (!failedUserAuth && !failedReaderAuth) {
+            result = EIC_ACCESS_CHECK_RESULT_OK;
+            break;
+        }
+        // One of the checks failed, convey which one
+        if (failedUserAuth) {
+            result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
+        } else {
+            result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
+        }
+    }
+    eicDebug("Result %d for name %s", result, name);
+
+    if (result == EIC_ACCESS_CHECK_RESULT_OK) {
+        eicCborAppendString(&ctx->cbor, name);
+    }
+    return result;
+}
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+                                       size_t encryptedContentSize, uint8_t* content,
+                                       const char* nameSpace, const char* name,
+                                       const int* accessControlProfileIds,
+                                       size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+                                       size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        calculatedSha256)) {
+        return false;
+    }
+    if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+        eicDebug("SHA-256 mismatch of additionalData");
+        return false;
+    }
+
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent, encryptedContentSize,
+                                additionalDataCbor, additionalDataCborSize, content)) {
+        eicDebug("Error decrypting content");
+        return false;
+    }
+
+    eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
+
+    return true;
+}
+
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+                                    size_t* digestToBeMacedSize) {
+    if (!ctx->buildCbor) {
+        *digestToBeMacedSize = 0;
+        return true;
+    }
+    if (*digestToBeMacedSize != 32) {
+        return false;
+    }
+
+    // This verifies that the correct expectedDeviceNamespacesSize value was
+    // passed in at eicPresentationCalcMacKey() time.
+    if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+        eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+        return false;
+    }
+    eicCborFinal(&ctx->cbor, digestToBeMaced);
+    return true;
+}
+
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+                                     size_t proofOfDeletionCborSize,
+                                     uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    EicCbor cbor;
+
+    eicCborInit(&cbor, NULL, 0);
+
+    // What we're going to sign is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    // Sig_structure = [
+    //   context : "Signature" / "Signature1" / "CounterSignature",
+    //   body_protected : empty_or_serialized_map,
+    //   ? sign_protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    //  ]
+    //
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
+
+    // Finally, the CBOR that we're actually signing.
+    eicCborAppendArray(&cbor, 3);
+    eicCborAppendString(&cbor, "ProofOfDeletion");
+    eicCborAppendString(&cbor, docType);
+    eicCborAppendBool(&cbor, ctx->testCredential);
+
+    uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, cborSha256);
+    if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+        eicDebug("Error signing proofOfDeletion");
+        return false;
+    }
+
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h
new file mode 100644
index 0000000..d798962
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+// The maximum size we support for public keys in reader certificates.
+#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
+
+typedef struct {
+    uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+    uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    // The challenge generated with eicPresentationCreateAuthChallenge()
+    uint64_t authChallenge;
+
+    // Set by eicPresentationSetAuthToken() and contains the fields
+    // from the passed in authToken and verificationToken.
+    //
+    uint64_t authTokenChallenge;
+    uint64_t authTokenSecureUserId;
+    uint64_t authTokenTimestamp;
+    uint64_t verificationTokenTimestamp;
+
+    // The public key for the reader.
+    //
+    // (During the process of pushing reader certificates, this is also used to store
+    // the public key of the previously pushed certificate.)
+    //
+    uint8_t readerPublicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+    size_t readerPublicKeySize;
+
+    // This is set to true only if eicPresentationValidateRequestMessage() successfully
+    // validated the requestMessage.
+    //
+    // Why even record this? Because there's no requirement the HAL actually calls that
+    // function and we validate ACPs before it's called... so it's possible that a
+    // compromised HAL could trick us into marking ACPs as authorized while they in fact
+    // aren't.
+    bool requestMessageValidated;
+    bool buildCbor;
+
+    // Set to true initialized as a test credential.
+    bool testCredential;
+
+    // These are bitmasks indicating which of the possible 32 access control profiles are
+    // authorized. They are built up by eicPresentationValidateAccessControlProfile().
+    //
+    uint32_t accessControlProfileMaskValidated;         // True if the profile was validated.
+    uint32_t accessControlProfileMaskUsesReaderAuth;    // True if the ACP is using reader auth
+    uint32_t accessControlProfileMaskFailedReaderAuth;  // True if failed reader auth
+    uint32_t accessControlProfileMaskFailedUserAuth;    // True if failed user auth
+
+    // SHA-256 for AdditionalData, updated for each entry.
+    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+    size_t expectedCborSizeAtEnd;
+    EicCbor cbor;
+} EicPresentation;
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+                         const uint8_t encryptedCredentialKeys[80]);
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+                                           uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+                                           uint8_t signingKeyBlob[60]);
+
+// Create an ephemeral key-pair.
+//
+// The private key is stored in |ctx->ephemeralPrivateKey| and also returned in
+// |ephemeralPrivateKey|.
+//
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+                                           uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]);
+
+// Returns a non-zero challenge in |authChallenge|.
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge);
+
+// Starts retrieveing entries.
+//
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx);
+
+// Sets the auth-token.
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+                                 uint64_t authenticatorId, int hardwareAuthenticatorType,
+                                 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+                                 uint64_t verificationTokenChallenge,
+                                 uint64_t verificationTokenTimeStamp,
+                                 int verificationTokenSecurityLevel,
+                                 const uint8_t* verificationTokenMac,
+                                 size_t verificationTokenMacSize);
+
+// Function to push certificates in the reader certificate chain.
+//
+// This should start with the root certificate (e.g. the last in the chain) and
+// continue up the chain, ending with the certificate for the reader.
+//
+// Calls to this function should be interleaved with calls to the
+// eicPresentationValidateAccessControlProfile() function, see below.
+//
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+                                   size_t certX509Size);
+
+// Checks an access control profile.
+//
+// Returns false if an error occurred while checking the profile (e.g. MAC doesn't check out).
+//
+// Returns in |accessGranted| whether access is granted.
+//
+// If |readerCertificate| is non-empty and the public key of one of those
+// certificates appear in the chain presented by the reader, this function must
+// be called after pushing that certificate using
+// eicPresentationPushReaderCert().
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+                                                 const uint8_t* readerCertificate,
+                                                 size_t readerCertificateSize,
+                                                 bool userAuthenticationRequired, int timeoutMillis,
+                                                 uint64_t secureUserId, const uint8_t mac[28],
+                                                 bool* accessGranted);
+
+// Validates that the given requestMessage is signed by the public key in the
+// certificate last set with eicPresentationPushReaderCert().
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// Must be called after eicPresentationPushReaderCert() have been used to push
+// the final certificate. Which is the certificate of the reader itself.
+//
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                                           size_t sessionTranscriptSize,
+                                           const uint8_t* requestMessage, size_t requestMessageSize,
+                                           int coseSignAlg,
+                                           const uint8_t* readerSignatureOfToBeSigned,
+                                           size_t readerSignatureOfToBeSignedSize);
+
+typedef enum {
+    // Returned if access is granted.
+    EIC_ACCESS_CHECK_RESULT_OK,
+
+    // Returned if an error occurred checking for access.
+    EIC_ACCESS_CHECK_RESULT_FAILED,
+
+    // Returned if access was denied because item is configured without any
+    // access control profiles.
+    EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES,
+
+    // Returned if access was denied because of user authentication.
+    EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED,
+
+    // Returned if access was denied because of reader authentication.
+    EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED,
+} EicAccessCheckResult;
+
+// Passes enough information to calculate the MACing key
+//
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                               size_t sessionTranscriptSize,
+                               const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+                               const uint8_t signingKeyBlob[60], const char* docType,
+                               unsigned int numNamespacesWithValues,
+                               size_t expectedDeviceNamespacesSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024
+// bytes, the bigger the better). It's done this way to avoid allocating stack
+// space.
+//
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+        EicPresentation* ctx, const char* nameSpace, const char* name,
+        unsigned int newNamespaceNumEntries, int32_t entrySize, const int* accessControlProfileIds,
+        size_t numAccessControlProfileIds, uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+                                       size_t encryptedContentSize, uint8_t* content,
+                                       const char* nameSpace, const char* name,
+                                       const int* accessControlProfileIds,
+                                       size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+                                       size_t scratchSpaceSize);
+
+// Returns the HMAC-SHA256 of |ToBeMaced| as per RFC 8051 "6.3. How to Compute
+// and Verify a MAC".
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+                                    size_t* digestToBeMacedSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfDeletion CBOR.
+//
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+                                     size_t proofOfDeletionCborSize,
+                                     uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
new file mode 100644
index 0000000..f16605c
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EicProvisioning.h"
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
+    eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+    ctx->testCredential = testCredential;
+    if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+                                        size_t challengeSize, const uint8_t* applicationId,
+                                        size_t applicationIdSize, uint8_t* publicKeyCert,
+                                        size_t* publicKeyCertSize) {
+    if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
+                                   applicationId, applicationIdSize, ctx->testCredential,
+                                   publicKeyCert, publicKeyCertSize)) {
+        return false;
+    }
+    return true;
+}
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+                                         const int* entryCounts, size_t numEntryCounts,
+                                         const char* docType,
+                                         size_t expectedProofOfProvisioningSize) {
+    if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
+        return false;
+    }
+    if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
+        return false;
+    }
+
+    ctx->numEntryCounts = numEntryCounts;
+    if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
+        return false;
+    }
+    for (size_t n = 0; n < numEntryCounts; n++) {
+        if (entryCounts[n] >= 256) {
+            return false;
+        }
+        ctx->entryCounts[n] = entryCounts[n];
+    }
+    ctx->curNamespace = -1;
+    ctx->curNamespaceNumProcessed = 0;
+
+    eicCborInit(&ctx->cbor, NULL, 0);
+
+    // What we're going to sign is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    // Sig_structure = [
+    //   context : "Signature" / "Signature1" / "CounterSignature",
+    //   body_protected : empty_or_serialized_map,
+    //   ? sign_protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    //  ]
+    //
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
+    ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+
+    eicCborAppendArray(&ctx->cbor, 5);
+    eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
+    eicCborAppendString(&ctx->cbor, docType);
+
+    eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
+
+    return true;
+}
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+                                            const uint8_t* readerCertificate,
+                                            size_t readerCertificateSize,
+                                            bool userAuthenticationRequired, uint64_t timeoutMillis,
+                                            uint64_t secureUserId, uint8_t outMac[28]) {
+    uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+    EicCbor cborBuilder;
+
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis, secureUserId)) {
+        return false;
+    }
+
+    // Calculate and return MAC
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0, cborBuilder.buffer,
+                                cborBuilder.size, outMac)) {
+        return false;
+    }
+
+    // The ACP CBOR in the provisioning receipt doesn't include secureUserId so build
+    // it again.
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis,
+                                  0 /* secureUserId */)) {
+        return false;
+    }
+
+    // Append the CBOR from the local builder to the digester.
+    eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
+
+    return true;
+}
+
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    // We'll need to calc and store a digest of additionalData to check that it's the same
+    // additionalData being passed in for every eicProvisioningAddEntryValue() call...
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        ctx->additionalDataSha256)) {
+        return false;
+    }
+
+    if (ctx->curNamespace == -1) {
+        ctx->curNamespace = 0;
+        ctx->curNamespaceNumProcessed = 0;
+        // Opens the main map: { * Namespace => [ + Entry ] }
+        eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        // Opens the per-namespace array: [ + Entry ]
+        eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+    }
+
+    if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
+        ctx->curNamespace += 1;
+        ctx->curNamespaceNumProcessed = 0;
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        // Opens the per-namespace array: [ + Entry ]
+        eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+    }
+
+    eicCborAppendMap(&ctx->cbor, 3);
+    eicCborAppendString(&ctx->cbor, "name");
+    eicCborAppendString(&ctx->cbor, name);
+
+    ctx->curEntrySize = entrySize;
+    ctx->curEntryNumBytesReceived = 0;
+
+    eicCborAppendString(&ctx->cbor, "value");
+
+    ctx->curNamespaceNumProcessed += 1;
+    return true;
+}
+
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, const uint8_t* content, size_t contentSize,
+                                  uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        calculatedSha256)) {
+        return false;
+    }
+    if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+        eicDebug("SHA-256 mismatch of additionalData");
+        return false;
+    }
+
+    eicCborAppend(&ctx->cbor, content, contentSize);
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize, additionalDataCbor,
+                                additionalDataCborSize, outEncryptedContent)) {
+        return false;
+    }
+
+    // If done with this entry, close the map
+    ctx->curEntryNumBytesReceived += contentSize;
+    if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
+        eicCborAppendString(&ctx->cbor, "accessControlProfiles");
+        eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
+        for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+            eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
+        }
+    }
+    return true;
+}
+
+bool eicProvisioningFinishAddingEntries(
+        EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+
+    eicCborAppendBool(&ctx->cbor, ctx->testCredential);
+    eicCborFinal(&ctx->cbor, cborSha256);
+
+    // This verifies that the correct expectedProofOfProvisioningSize value was
+    // passed in at eicStartPersonalization() time.
+    if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+        eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+        return false;
+    }
+
+    if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+        eicDebug("Error signing proofOfProvisioning");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+                                            uint8_t encryptedCredentialKeys[80]) {
+    EicCbor cbor;
+    uint8_t cborBuf[52];
+
+    eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
+    eicCborAppendArray(&cbor, 2);
+    eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
+    eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+    if (cbor.size > sizeof(cborBuf)) {
+        eicDebug("Exceeded buffer size");
+        return false;
+    }
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        eicDebug("Error getting random");
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(
+                eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size,
+                // DocType is the additionalAuthenticatedData
+                (const uint8_t*)docType, eicStrLen(docType), encryptedCredentialKeys)) {
+        eicDebug("Error encrypting CredentialKeys");
+        return false;
+    }
+
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h
new file mode 100644
index 0000000..836d16e
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+#define EIC_MAX_NUM_NAMESPACES 32
+#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
+
+typedef struct {
+    // Set by eicCreateCredentialKey.
+    uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    int numEntryCounts;
+    uint8_t entryCounts[EIC_MAX_NUM_NAMESPACES];
+
+    int curNamespace;
+    int curNamespaceNumProcessed;
+
+    size_t curEntrySize;
+    size_t curEntryNumBytesReceived;
+
+    uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+
+    size_t expectedCborSizeAtEnd;
+
+    // SHA-256 for AdditionalData, updated for each entry.
+    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+    EicCbor cbor;
+
+    bool testCredential;
+} EicProvisioning;
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+                                        size_t challengeSize, const uint8_t* applicationId,
+                                        size_t applicationIdSize, uint8_t* publicKeyCert,
+                                        size_t* publicKeyCertSize);
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+                                         const int* entryCounts, size_t numEntryCounts,
+                                         const char* docType,
+                                         size_t expectedProofOfProvisioningingSize);
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+                                            const uint8_t* readerCertificate,
+                                            size_t readerCertificateSize,
+                                            bool userAuthenticationRequired, uint64_t timeoutMillis,
+                                            uint64_t secureUserId, uint8_t outMac[28]);
+
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize);
+
+// The outEncryptedContent array must be contentSize + 28 bytes long.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, const uint8_t* content, size_t contentSize,
+                                  uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfProvisioninging CBOR.
+//
+bool eicProvisioningFinishAddingEntries(
+        EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+//
+//
+// The |encryptedCredentialKeys| array is set to AES-GCM-ENC(HBK, R, CredentialKeys, docType)
+// where
+//
+//   CredentialKeys = [
+//     bstr,   ; storageKey, a 128-bit AES key
+//     bstr    ; credentialPrivKey, the private key for credentialKey
+//   ]
+//
+// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
+// encoded CBOR for CredentialKeys is 52 bytes and consequently
+// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
+//
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+                                            uint8_t encryptedCredentialKeys[80]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
diff --git a/identity/aidl/default/libeic/libeic.h b/identity/aidl/default/libeic/libeic.h
new file mode 100644
index 0000000..88abef8
--- /dev/null
+++ b/identity/aidl/default/libeic/libeic.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+#define ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The EIC_INSIDE_LIBEIC_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+#define EIC_INSIDE_LIBEIC_H
+#include "EicCbor.h"
+#include "EicOps.h"
+#include "EicPresentation.h"
+#include "EicProvisioning.h"
+#undef EIC_INSIDE_LIBEIC_H
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_LIBEIC_H
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
index bf95df5..c290c08 100644
--- a/identity/aidl/default/service.cpp
+++ b/identity/aidl/default/service.cpp
@@ -22,20 +22,26 @@
 
 #include "IdentityCredentialStore.h"
 
+#include "FakeSecureHardwareProxy.h"
+
+using ::android::sp;
 using ::android::base::InitLogging;
 using ::android::base::StderrLogger;
 
-using aidl::android::hardware::identity::IdentityCredentialStore;
+using ::aidl::android::hardware::identity::IdentityCredentialStore;
+using ::android::hardware::identity::FakeSecureHardwareProxyFactory;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
 
 int main(int /*argc*/, char* argv[]) {
     InitLogging(argv, StderrLogger);
 
+    sp<SecureHardwareProxyFactory> hwProxyFactory = new FakeSecureHardwareProxyFactory();
+
     ABinderProcess_setThreadPoolMaxThreadCount(0);
     std::shared_ptr<IdentityCredentialStore> store =
-            ndk::SharedRefBase::make<IdentityCredentialStore>();
+            ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory);
 
     const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
-    LOG(INFO) << "instance: " << instance;
     binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
     CHECK(status == STATUS_OK);