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/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