diff options
Diffstat (limited to 'identity/aidl/default/libeic')
-rw-r--r-- | identity/aidl/default/libeic/EicCbor.c | 44 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicCbor.h | 24 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicCommon.h | 47 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicOps.h | 36 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicPresentation.c | 209 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicPresentation.h | 54 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicProvisioning.c | 113 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicProvisioning.h | 36 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicSession.c | 120 | ||||
-rw-r--r-- | identity/aidl/default/libeic/EicSession.h | 73 | ||||
-rw-r--r-- | identity/aidl/default/libeic/libeic.h | 2 |
11 files changed, 605 insertions, 153 deletions
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c index 0e2684fec4..b9304bcacb 100644 --- a/identity/aidl/default/libeic/EicCbor.c +++ b/identity/aidl/default/libeic/EicCbor.c @@ -91,7 +91,7 @@ size_t eicCborAdditionalLengthBytesFor(size_t size) { return 8; } -void eicCborBegin(EicCbor* cbor, int majorType, size_t size) { +void eicCborBegin(EicCbor* cbor, int majorType, uint64_t size) { uint8_t data[9]; if (size < 24) { @@ -132,10 +132,13 @@ void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t 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 eicCborAppendString(EicCbor* cbor, const char* str, size_t strLength) { + eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_STRING, strLength); + eicCborAppend(cbor, (const uint8_t*)str, strLength); +} + +void eicCborAppendStringZ(EicCbor* cbor, const char* str) { + eicCborAppendString(cbor, str, eicStrLen(str)); } void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue) { @@ -153,13 +156,13 @@ void eicCborAppendSemantic(EicCbor* cbor, uint64_t value) { } void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value) { - size_t encoded = value; + uint64_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; + uint64_t encoded = -1 - value; eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_NEGATIVE, encoded); } else { eicCborAppendUnsigned(cbor, value); @@ -188,19 +191,19 @@ bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* reade } } eicCborAppendMap(cborBuilder, numPairs); - eicCborAppendString(cborBuilder, "id"); + eicCborAppendStringZ(cborBuilder, "id"); eicCborAppendUnsigned(cborBuilder, id); if (readerCertificateSize > 0) { - eicCborAppendString(cborBuilder, "readerCertificate"); + eicCborAppendStringZ(cborBuilder, "readerCertificate"); eicCborAppendByteString(cborBuilder, readerCertificate, readerCertificateSize); } if (userAuthenticationRequired) { - eicCborAppendString(cborBuilder, "userAuthenticationRequired"); + eicCborAppendStringZ(cborBuilder, "userAuthenticationRequired"); eicCborAppendBool(cborBuilder, userAuthenticationRequired); - eicCborAppendString(cborBuilder, "timeoutMillis"); + eicCborAppendStringZ(cborBuilder, "timeoutMillis"); eicCborAppendUnsigned(cborBuilder, timeoutMillis); if (secureUserId > 0) { - eicCborAppendString(cborBuilder, "secureUserId"); + eicCborAppendStringZ(cborBuilder, "secureUserId"); eicCborAppendUnsigned(cborBuilder, secureUserId); } } @@ -214,20 +217,21 @@ bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* reade return true; } -bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds, +bool eicCborCalcEntryAdditionalData(const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, uint8_t* cborBuffer, size_t cborBufferSize, - size_t* outAdditionalDataCborSize, + size_t nameSpaceLength, const char* name, + size_t nameLength, 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"); + eicCborAppendStringZ(&cborBuilder, "Namespace"); + eicCborAppendString(&cborBuilder, nameSpace, nameSpaceLength); + eicCborAppendStringZ(&cborBuilder, "Name"); + eicCborAppendString(&cborBuilder, name, nameLength); + eicCborAppendStringZ(&cborBuilder, "AccessControlProfileIds"); eicCborAppendArray(&cborBuilder, numAccessControlProfileIds); for (size_t n = 0; n < numAccessControlProfileIds; n++) { eicCborAppendNumber(&cborBuilder, accessControlProfileIds[n]); diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h index 9c0f531e4a..16f7ab6619 100644 --- a/identity/aidl/default/libeic/EicCbor.h +++ b/identity/aidl/default/libeic/EicCbor.h @@ -102,13 +102,16 @@ void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size); #define EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR 24 /* Begins a new CBOR value. */ -void eicCborBegin(EicCbor* cbor, int majorType, size_t size); +void eicCborBegin(EicCbor* cbor, int majorType, uint64_t size); /* Appends a bytestring. */ void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize); +/* Appends a UTF-8 string. */ +void eicCborAppendString(EicCbor* cbor, const char* str, size_t strLength); + /* Appends a NUL-terminated UTF-8 string. */ -void eicCborAppendString(EicCbor* cbor, const char* str); +void eicCborAppendStringZ(EicCbor* cbor, const char* str); /* Appends a simple value. */ void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue); @@ -144,22 +147,13 @@ bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* reade size_t readerCertificateSize, bool userAuthenticationRequired, uint64_t timeoutMillis, uint64_t secureUserId); -bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds, +bool eicCborCalcEntryAdditionalData(const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, uint8_t* cborBuffer, size_t cborBufferSize, - size_t* outAdditionalDataCborSize, + size_t nameSpaceLength, const char* name, + size_t nameLength, 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 diff --git a/identity/aidl/default/libeic/EicCommon.h b/identity/aidl/default/libeic/EicCommon.h new file mode 100644 index 0000000000..2a08a35f65 --- /dev/null +++ b/identity/aidl/default/libeic/EicCommon.h @@ -0,0 +1,47 @@ +/* + * 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_COMMON_H +#define ANDROID_HARDWARE_IDENTITY_EIC_COMMON_H + +// KeyMint auth-challenges are 64-bit numbers and 0 typically means unset. +#define EIC_KM_AUTH_CHALLENGE_UNSET 0 + +// Feature version 202009: +// +// CredentialKeys = [ +// bstr, ; storageKey, a 128-bit AES key +// bstr, ; credentialPrivKey, the private key for credentialKey +// ] +// +// Feature version 202101: +// +// CredentialKeys = [ +// bstr, ; storageKey, a 128-bit AES key +// bstr, ; credentialPrivKey, the private key for credentialKey +// bstr ; proofOfProvisioning SHA-256 +// ] +// +// where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning +// SHA-256 is 32 bytes. +#define EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 52 +#define EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 86 + +#endif // ANDROID_HARDWARE_IDENTITY_EIC_COMMON_H diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h index d4fcf0e1bb..df96c7db48 100644 --- a/identity/aidl/default/libeic/EicOps.h +++ b/identity/aidl/default/libeic/EicOps.h @@ -141,6 +141,10 @@ void* eicMemCpy(void* dest, const void* src, size_t n); // String length, see strlen(3). size_t eicStrLen(const char* s); +// Locate a substring, see memmem(3) +void* eicMemMem(const uint8_t* haystack, size_t haystackLen, const uint8_t* needle, + size_t needleLen); + // Memory compare, see CRYPTO_memcmp(3SSL) // // It takes an amount of time dependent on len, but independent of the contents of the @@ -151,6 +155,12 @@ int eicCryptoMemCmp(const void* s1, const void* s2, size_t n); // Random number generation. bool eicOpsRandom(uint8_t* buf, size_t numBytes); +// Creates a new non-zero identifier in |id|. +// +// Is guaranteed to be non-zero and different than what is already in |id|. +// +bool eicNextId(uint32_t* id); + // If |testCredential| is true, returns the 128-bit AES Hardware-Bound Key (16 bytes). // // Otherwise returns all zeroes (16 bytes). @@ -186,13 +196,19 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_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|. +// If |attestationKeyBlob| is non-NULL, the certificate must be signed by the +// the provided attestation key. Else, the certificate must be signed by the +// attestation key that the secure area has been factory provisioned with. The +// given |challenge|, |applicationId|, and |testCredential| must be signed +// into the attestation. +// +// When |attestationKeyBlob| is non-NULL, then |attestationKeyCert| must +// also be passed so that the underlying implementation can properly chain up +// the newly-generated certificate to the existing chain. // -// 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. +// The generated certificate must be in X.509 format and returned in |cert| +// and |certSize| must be set to the size of this array. This function must +// set |certSize| 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 @@ -201,8 +217,10 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], // 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 + size_t applicationIdSize, bool testCredential, + const uint8_t* attestationKeyBlob, size_t attestationKeyBlobSize, + const uint8_t* attestationKeyCert, size_t attestationKeyCertSize, + uint8_t* /*out*/ cert, size_t* /*inout*/ certSize); // Generate an X.509 certificate for the key identified by |publicKey| which // must be of the form returned by eicOpsCreateEcKey(). @@ -295,6 +313,8 @@ bool eicOpsValidateAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t int verificationTokenSecurityLevel, const uint8_t* verificationTokenMac, size_t verificationTokenMacSize); +// Also see eicOpsLookupActiveSessionFromId() defined in EicSession.h + #ifdef __cplusplus } #endif diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c index 3d13766f1e..104a559697 100644 --- a/identity/aidl/default/libeic/EicPresentation.c +++ b/identity/aidl/default/libeic/EicPresentation.c @@ -15,22 +15,29 @@ */ #include "EicPresentation.h" +#include "EicCommon.h" +#include "EicSession.h" #include <inttypes.h> -bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType, +// Global used for assigning ids for presentation objects. +// +static uint32_t gPresentationLastIdAssigned = 0; + +bool eicPresentationInit(EicPresentation* ctx, uint32_t sessionId, bool testCredential, + const char* docType, size_t docTypeLength, const uint8_t* encryptedCredentialKeys, size_t encryptedCredentialKeysSize) { - uint8_t credentialKeys[86]; + uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101]; bool expectPopSha256 = false; // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86 // bytes (the additional data is the ProofOfProvisioning SHA-256). We need // to support loading all feature versions. // - if (encryptedCredentialKeysSize == 52 + 28) { + if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) { /* do nothing */ - } else if (encryptedCredentialKeysSize == 86 + 28) { + } else if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) { expectPopSha256 = true; } else { eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize); @@ -38,11 +45,18 @@ bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* } eicMemSet(ctx, '\0', sizeof(EicPresentation)); + ctx->sessionId = sessionId; + + if (!eicNextId(&gPresentationLastIdAssigned)) { + eicDebug("Error getting id for object"); + return false; + } + ctx->id = gPresentationLastIdAssigned; if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys, encryptedCredentialKeysSize, // DocType is the additionalAuthenticatedData - (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) { + (const uint8_t*)docType, docTypeLength, credentialKeys)) { eicDebug("Error decrypting CredentialKeys"); return false; } @@ -85,10 +99,28 @@ bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* if (expectPopSha256) { eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE); } + + eicDebug("Initialized presentation with id %" PRIu32, ctx->id); + return true; +} + +bool eicPresentationShutdown(EicPresentation* ctx) { + if (ctx->id == 0) { + eicDebug("Trying to shut down presentation with id 0"); + return false; + } + eicDebug("Shut down presentation with id %" PRIu32, ctx->id); + eicMemSet(ctx, '\0', sizeof(EicPresentation)); + return true; +} + +bool eicPresentationGetId(EicPresentation* ctx, uint32_t* outId) { + *outId = ctx->id; return true; } -bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now, +bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, + size_t docTypeLength, time_t now, uint8_t* publicKeyCert, size_t* publicKeyCertSize, uint8_t signingKeyBlob[60]) { uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE]; @@ -114,7 +146,7 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc EicCbor cbor; eicCborInit(&cbor, cborBuf, sizeof cborBuf); eicCborAppendArray(&cbor, 2); - eicCborAppendString(&cbor, "ProofOfBinding"); + eicCborAppendStringZ(&cbor, "ProofOfBinding"); eicCborAppendByteString(&cbor, ctx->proofOfProvisioningSha256, EIC_SHA256_DIGEST_SIZE); if (cbor.size > sizeof(cborBuf)) { eicDebug("Exceeded buffer size"); @@ -147,7 +179,7 @@ bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* doc } if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv), // DocType is the additionalAuthenticatedData - (const uint8_t*)docType, eicStrLen(docType), signingKeyBlob)) { + (const uint8_t*)docType, docTypeLength, signingKeyBlob)) { eicDebug("Error encrypting signing key"); return false; } @@ -172,7 +204,7 @@ bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChal eicDebug("Failed generating random challenge"); return false; } - } while (ctx->authChallenge == 0); + } while (ctx->authChallenge == EIC_KM_AUTH_CHALLENGE_UNSET); eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge); *authChallenge = ctx->authChallenge; return true; @@ -188,6 +220,24 @@ bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* int coseSignAlg, const uint8_t* readerSignatureOfToBeSigned, size_t readerSignatureOfToBeSignedSize) { + if (ctx->sessionId != 0) { + EicSession* session = eicSessionGetForId(ctx->sessionId); + if (session == NULL) { + eicDebug("Error looking up session for sessionId %" PRIu32, ctx->sessionId); + return false; + } + EicSha256Ctx sha256; + uint8_t sessionTranscriptSha256[EIC_SHA256_DIGEST_SIZE]; + eicOpsSha256Init(&sha256); + eicOpsSha256Update(&sha256, sessionTranscript, sessionTranscriptSize); + eicOpsSha256Final(&sha256, sessionTranscriptSha256); + if (eicCryptoMemCmp(sessionTranscriptSha256, session->sessionTranscriptSha256, + EIC_SHA256_DIGEST_SIZE) != 0) { + eicDebug("SessionTranscript mismatch"); + return false; + } + } + if (ctx->readerPublicKeySize == 0) { eicDebug("No public key for reader"); return false; @@ -219,7 +269,7 @@ bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* EicCbor cbor; eicCborInit(&cbor, NULL, 0); eicCborAppendArray(&cbor, 4); - eicCborAppendString(&cbor, "Signature1"); + eicCborAppendStringZ(&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 @@ -277,7 +327,7 @@ bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* // size_t payloadOffset = cbor.size; eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3); - eicCborAppendString(&cbor, "ReaderAuthentication"); + eicCborAppendStringZ(&cbor, "ReaderAuthentication"); eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize); eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR); eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize); @@ -328,6 +378,20 @@ bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509 return true; } +static bool getChallenge(EicPresentation* ctx, uint64_t* outAuthChallenge) { + // Use authChallenge from session if applicable. + *outAuthChallenge = ctx->authChallenge; + if (ctx->sessionId != 0) { + EicSession* session = eicSessionGetForId(ctx->sessionId); + if (session == NULL) { + eicDebug("Error looking up session for sessionId %" PRIu32, ctx->sessionId); + return false; + } + *outAuthChallenge = session->authChallenge; + } + 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, @@ -336,14 +400,19 @@ bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint6 int verificationTokenSecurityLevel, const uint8_t* verificationTokenMac, size_t verificationTokenMacSize) { + uint64_t authChallenge; + if (!getChallenge(ctx, &authChallenge)) { + return false; + } + // It doesn't make sense to accept any tokens if eicPresentationCreateAuthChallenge() // was never called. - if (ctx->authChallenge == 0) { - eicDebug("Trying validate tokens when no auth-challenge was previously generated"); + if (authChallenge == EIC_KM_AUTH_CHALLENGE_UNSET) { + eicDebug("Trying to validate tokens when no auth-challenge was previously generated"); return false; } // At least the verification-token must have the same challenge as what was generated. - if (verificationTokenChallenge != ctx->authChallenge) { + if (verificationTokenChallenge != authChallenge) { eicDebug("Challenge in verification token does not match the challenge " "previously generated"); return false; @@ -352,6 +421,7 @@ bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint6 challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac, macSize, verificationTokenChallenge, verificationTokenTimestamp, verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) { + eicDebug("Error validating authToken"); return false; } ctx->authTokenChallenge = challenge; @@ -375,11 +445,16 @@ static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired, // Only ACP with auth-on-every-presentation - those with timeout == 0 - need the // challenge to match... if (timeoutMillis == 0) { - if (ctx->authTokenChallenge != ctx->authChallenge) { + uint64_t authChallenge; + if (!getChallenge(ctx, &authChallenge)) { + return false; + } + + if (ctx->authTokenChallenge != authChallenge) { eicDebug("Challenge in authToken (%" PRIu64 ") doesn't match the challenge " "that was created (%" PRIu64 ") for this session", - ctx->authTokenChallenge, ctx->authChallenge); + ctx->authTokenChallenge, authChallenge); return false; } } @@ -438,18 +513,18 @@ bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id, size_t readerCertificateSize, bool userAuthenticationRequired, int timeoutMillis, uint64_t secureUserId, const uint8_t mac[28], - bool* accessGranted) { + bool* accessGranted, + uint8_t* scratchSpace, + size_t scratchSpaceSize) { *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); + eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize); if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize, userAuthenticationRequired, timeoutMillis, secureUserId)) { return false; @@ -464,15 +539,15 @@ bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id, checkUserAuth(ctx, userAuthenticationRequired, timeoutMillis, secureUserId); bool passedReaderAuth = checkReaderAuth(ctx, readerCertificate, readerCertificateSize); - ctx->accessControlProfileMaskValidated |= (1 << id); + ctx->accessControlProfileMaskValidated |= (1U << id); if (readerCertificateSize > 0) { - ctx->accessControlProfileMaskUsesReaderAuth |= (1 << id); + ctx->accessControlProfileMaskUsesReaderAuth |= (1U << id); } if (!passedReaderAuth) { - ctx->accessControlProfileMaskFailedReaderAuth |= (1 << id); + ctx->accessControlProfileMaskFailedReaderAuth |= (1U << id); } if (!passedUserAuth) { - ctx->accessControlProfileMaskFailedUserAuth |= (1 << id); + ctx->accessControlProfileMaskFailedUserAuth |= (1U << id); } if (passedUserAuth && passedReaderAuth) { @@ -486,11 +561,30 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans 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 docTypeLength, unsigned int numNamespacesWithValues, size_t expectedDeviceNamespacesSize) { + if (ctx->sessionId != 0) { + EicSession* session = eicSessionGetForId(ctx->sessionId); + if (session == NULL) { + eicDebug("Error looking up session for sessionId %" PRIu32, ctx->sessionId); + return false; + } + EicSha256Ctx sha256; + uint8_t sessionTranscriptSha256[EIC_SHA256_DIGEST_SIZE]; + eicOpsSha256Init(&sha256); + eicOpsSha256Update(&sha256, sessionTranscript, sessionTranscriptSize); + eicOpsSha256Final(&sha256, sessionTranscriptSha256); + if (eicCryptoMemCmp(sessionTranscriptSha256, session->sessionTranscriptSha256, + EIC_SHA256_DIGEST_SIZE) != 0) { + eicDebug("SessionTranscript mismatch"); + return false; + } + readerEphemeralPublicKey = session->readerEphemeralPublicKey; + } + uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE]; if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType, - eicStrLen(docType), signingKeyPriv)) { + docTypeLength, signingKeyPriv)) { eicDebug("Error decrypting signingKeyBlob"); return false; } @@ -530,7 +624,7 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans // ] // eicCborAppendArray(&ctx->cbor, 4); - eicCborAppendString(&ctx->cbor, "MAC0"); + eicCborAppendStringZ(&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 @@ -566,8 +660,7 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans 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 += 1 + eicCborAdditionalLengthBytesFor(docTypeLength) + docTypeLength; calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24) calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize); calculatedSize += expectedDeviceNamespacesSize; @@ -589,9 +682,9 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize); eicCborAppendArray(&ctx->cbor, 4); - eicCborAppendString(&ctx->cbor, "DeviceAuthentication"); + eicCborAppendStringZ(&ctx->cbor, "DeviceAuthentication"); eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize); - eicCborAppendString(&ctx->cbor, docType); + eicCborAppendString(&ctx->cbor, docType, docTypeLength); // 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 @@ -618,16 +711,18 @@ bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) { } EicAccessCheckResult eicPresentationStartRetrieveEntryValue( - EicPresentation* ctx, const char* nameSpace, const char* name, - unsigned int newNamespaceNumEntries, int32_t /* entrySize */, - const int* accessControlProfileIds, size_t numAccessControlProfileIds, + EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength, + const char* name, size_t nameLength, + unsigned int newNamespaceNumEntries, int32_t entrySize, + const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, uint8_t* scratchSpace, size_t scratchSpaceSize) { + (void)entrySize; uint8_t* additionalDataCbor = scratchSpace; - const size_t additionalDataCborBufSize = scratchSpaceSize; + size_t additionalDataCborBufferSize = scratchSpaceSize; size_t additionalDataCborSize; if (newNamespaceNumEntries > 0) { - eicCborAppendString(&ctx->cbor, nameSpace); + eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength); eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries); } @@ -636,8 +731,9 @@ EicAccessCheckResult eicPresentationStartRetrieveEntryValue( // ctx->accessCheckOk = false; if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds, - nameSpace, name, additionalDataCbor, - additionalDataCborBufSize, &additionalDataCborSize, + nameSpace, nameSpaceLength, name, nameLength, + additionalDataCbor, additionalDataCborBufferSize, + &additionalDataCborSize, ctx->additionalDataSha256)) { return EIC_ACCESS_CHECK_RESULT_FAILED; } @@ -681,7 +777,7 @@ EicAccessCheckResult eicPresentationStartRetrieveEntryValue( eicDebug("Result %d for name %s", result, name); if (result == EIC_ACCESS_CHECK_RESULT_OK) { - eicCborAppendString(&ctx->cbor, name); + eicCborAppendString(&ctx->cbor, name, nameLength); ctx->accessCheckOk = true; } return result; @@ -690,18 +786,21 @@ EicAccessCheckResult eicPresentationStartRetrieveEntryValue( // 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, + const char* nameSpace, size_t nameSpaceLength, + const char* name, size_t nameLength, + const uint8_t* accessControlProfileIds, + size_t numAccessControlProfileIds, + uint8_t* scratchSpace, size_t scratchSpaceSize) { uint8_t* additionalDataCbor = scratchSpace; - const size_t additionalDataCborBufSize = scratchSpaceSize; + size_t additionalDataCborBufferSize = scratchSpaceSize; size_t additionalDataCborSize; uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE]; if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds, - nameSpace, name, additionalDataCbor, - additionalDataCborBufSize, &additionalDataCborSize, + nameSpace, nameSpaceLength, name, nameLength, + additionalDataCbor, additionalDataCborBufferSize, + &additionalDataCborSize, calculatedSha256)) { return false; } @@ -746,9 +845,10 @@ bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMac return true; } -bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, +bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, size_t docTypeLength, const uint8_t* challenge, size_t challengeSize, - bool includeChallenge, size_t proofOfDeletionCborSize, + bool includeChallenge, + size_t proofOfDeletionCborSize, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) { EicCbor cbor; @@ -766,7 +866,7 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, // ] // eicCborAppendArray(&cbor, 4); - eicCborAppendString(&cbor, "Signature1"); + eicCborAppendStringZ(&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 @@ -787,8 +887,8 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, // Finally, the CBOR that we're actually signing. eicCborAppendArray(&cbor, includeChallenge ? 4 : 3); - eicCborAppendString(&cbor, "ProofOfDeletion"); - eicCborAppendString(&cbor, docType); + eicCborAppendStringZ(&cbor, "ProofOfDeletion"); + eicCborAppendString(&cbor, docType, docTypeLength); if (includeChallenge) { eicCborAppendByteString(&cbor, challenge, challengeSize); } @@ -804,7 +904,8 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, return true; } -bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential, +bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, + size_t docTypeLength, bool testCredential, const uint8_t* challenge, size_t challengeSize, size_t proofOfOwnershipCborSize, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) { @@ -824,7 +925,7 @@ bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bo // ] // eicCborAppendArray(&cbor, 4); - eicCborAppendString(&cbor, "Signature1"); + eicCborAppendStringZ(&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 @@ -845,8 +946,8 @@ bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bo // Finally, the CBOR that we're actually signing. eicCborAppendArray(&cbor, 4); - eicCborAppendString(&cbor, "ProofOfOwnership"); - eicCborAppendString(&cbor, docType); + eicCborAppendStringZ(&cbor, "ProofOfOwnership"); + eicCborAppendString(&cbor, docType, docTypeLength); eicCborAppendByteString(&cbor, challenge, challengeSize); eicCborAppendBool(&cbor, testCredential); diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h index c888049dbe..a031890e58 100644 --- a/identity/aidl/default/libeic/EicPresentation.h +++ b/identity/aidl/default/libeic/EicPresentation.h @@ -30,7 +30,13 @@ extern "C" { // The maximum size we support for public keys in reader certificates. #define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65 +// Constant used to convey that no session is associated with a presentation. +#define EIC_PRESENTATION_ID_UNSET 0 + typedef struct { + // A non-zero number unique for this EicPresentation instance + uint32_t id; + int featureLevel; uint8_t storageKey[EIC_AES_128_KEY_SIZE]; @@ -38,6 +44,10 @@ typedef struct { uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]; + // If non-zero (not EIC_PRESENTATION_ID_UNSET), the id of the EicSession object this + // presentation object is associated with. + uint32_t sessionId; + // The challenge generated with eicPresentationCreateAuthChallenge() uint64_t authChallenge; @@ -93,11 +103,20 @@ typedef struct { EicCbor cbor; } EicPresentation; -bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType, +// If sessionId is zero (EIC_PRESENTATION_ID_UNSET), the presentation object is not associated +// with a session object. Otherwise it's the id of the session object. +// +bool eicPresentationInit(EicPresentation* ctx, uint32_t sessionId, bool testCredential, + const char* docType, size_t docTypeLength, const uint8_t* encryptedCredentialKeys, size_t encryptedCredentialKeysSize); -bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now, +bool eicPresentationShutdown(EicPresentation* ctx); + +bool eicPresentationGetId(EicPresentation* ctx, uint32_t* outId); + +bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, + size_t docTypeLength, time_t now, uint8_t* publicKeyCert, size_t* publicKeyCertSize, uint8_t signingKeyBlob[60]); @@ -148,12 +167,17 @@ bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509 // be called after pushing that certificate using // eicPresentationPushReaderCert(). // +// The scratchSpace should be set to a buffer at least 512 bytes. It's done +// this way to avoid allocating stack space. +// 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); + bool* accessGranted, + uint8_t* scratchSpace, + size_t scratchSpaceSize); // Validates that the given requestMessage is signed by the public key in the // certificate last set with eicPresentationPushReaderCert(). @@ -196,7 +220,7 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans 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 docTypeLength, unsigned int numNamespacesWithValues, size_t expectedDeviceNamespacesSize); // The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024 @@ -204,9 +228,11 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans // 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); + EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength, + const char* name, size_t nameLength, + unsigned int newNamespaceNumEntries, int32_t entrySize, + const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, + uint8_t* scratchSpace, size_t scratchSpaceSize); // Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes. // @@ -215,9 +241,11 @@ EicAccessCheckResult eicPresentationStartRetrieveEntryValue( // 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, + const char* nameSpace, size_t nameSpaceLength, + const char* name, size_t nameLength, + const uint8_t* 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 @@ -229,7 +257,7 @@ bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMac // 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, +bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, size_t docTypeLength, const uint8_t* challenge, size_t challengeSize, bool includeChallenge, size_t proofOfDeletionCborSize, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]); @@ -238,8 +266,8 @@ bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, // the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process" // where content is set to the ProofOfOwnership CBOR. // -bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential, - const uint8_t* challenge, size_t challengeSize, +bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, size_t docTypeLength, + bool testCredential, const uint8_t* challenge, size_t challengeSize, size_t proofOfOwnershipCborSize, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]); diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c index 3b4148e571..ff009dde6b 100644 --- a/identity/aidl/default/libeic/EicProvisioning.c +++ b/identity/aidl/default/libeic/EicProvisioning.c @@ -15,9 +15,23 @@ */ #include "EicProvisioning.h" +#include "EicCommon.h" + +#include <inttypes.h> + +// Global used for assigning ids for provisioning objects. +// +static uint32_t gProvisioningLastIdAssigned = 0; bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) { eicMemSet(ctx, '\0', sizeof(EicProvisioning)); + + if (!eicNextId(&gProvisioningLastIdAssigned)) { + eicDebug("Error getting id for object"); + return false; + } + ctx->id = gProvisioningLastIdAssigned; + ctx->testCredential = testCredential; if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) { return false; @@ -27,18 +41,18 @@ bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) { } bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType, - const uint8_t* encryptedCredentialKeys, + size_t docTypeLength, const uint8_t* encryptedCredentialKeys, size_t encryptedCredentialKeysSize) { - uint8_t credentialKeys[86]; + uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101]; // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86 // bytes (the additional data is the ProofOfProvisioning SHA-256). We need // to support loading all feature versions. // bool expectPopSha256 = false; - if (encryptedCredentialKeysSize == 52 + 28) { + if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) { /* do nothing */ - } else if (encryptedCredentialKeysSize == 86 + 28) { + } else if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) { expectPopSha256 = true; } else { eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize); @@ -46,12 +60,19 @@ bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, con } eicMemSet(ctx, '\0', sizeof(EicProvisioning)); + + if (!eicNextId(&gProvisioningLastIdAssigned)) { + eicDebug("Error getting id for object"); + return false; + } + ctx->id = gProvisioningLastIdAssigned; + ctx->testCredential = testCredential; if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys, encryptedCredentialKeysSize, // DocType is the additionalAuthenticatedData - (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) { + (const uint8_t*)docType, docTypeLength, credentialKeys)) { eicDebug("Error decrypting CredentialKeys"); return false; } @@ -95,9 +116,27 @@ bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, con return true; } +bool eicProvisioningShutdown(EicProvisioning* ctx) { + if (ctx->id == 0) { + eicDebug("Trying to shut down provsioning with id 0"); + return false; + } + eicDebug("Shut down provsioning with id %" PRIu32, ctx->id); + eicMemSet(ctx, '\0', sizeof(EicProvisioning)); + return true; +} + +bool eicProvisioningGetId(EicProvisioning* ctx, uint32_t* outId) { + *outId = ctx->id; + 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 applicationIdSize, const uint8_t* attestationKeyBlob, + size_t attestationKeyBlobSize, + const uint8_t* attestationKeyCert, + size_t attestationKeyCertSize, uint8_t* publicKeyCert, size_t* publicKeyCertSize) { if (ctx->isUpdate) { eicDebug("Cannot create CredentialKey on update"); @@ -106,7 +145,9 @@ bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* cha if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize, applicationId, applicationIdSize, ctx->testCredential, - publicKeyCert, publicKeyCertSize)) { + attestationKeyBlob, attestationKeyBlobSize, attestationKeyCert, + attestationKeyCertSize, publicKeyCert, publicKeyCertSize)) { + eicDebug("Error creating credential key"); return false; } return true; @@ -114,7 +155,7 @@ bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* cha bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount, const int* entryCounts, size_t numEntryCounts, - const char* docType, + const char* docType, size_t docTypeLength, size_t expectedProofOfProvisioningSize) { if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) { return false; @@ -150,7 +191,7 @@ bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControl // ] // eicCborAppendArray(&ctx->cbor, 4); - eicCborAppendString(&ctx->cbor, "Signature1"); + eicCborAppendStringZ(&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 @@ -174,8 +215,8 @@ bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControl eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester); eicCborAppendArray(&ctx->cbor, 5); - eicCborAppendString(&ctx->cbor, "ProofOfProvisioning"); - eicCborAppendString(&ctx->cbor, docType); + eicCborAppendStringZ(&ctx->cbor, "ProofOfProvisioning"); + eicCborAppendString(&ctx->cbor, docType, docTypeLength); eicCborAppendArray(&ctx->cbor, accessControlProfileCount); @@ -185,12 +226,12 @@ bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControl 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]; + bool userAuthenticationRequired, + uint64_t timeoutMillis, uint64_t secureUserId, + uint8_t outMac[28], uint8_t* scratchSpace, + size_t scratchSpaceSize) { EicCbor cborBuilder; - - eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE); + eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize); if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize, userAuthenticationRequired, timeoutMillis, secureUserId)) { @@ -209,7 +250,7 @@ bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id, // 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); + eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize); if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize, userAuthenticationRequired, timeoutMillis, 0 /* secureUserId */)) { @@ -222,9 +263,10 @@ bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id, return true; } -bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds, +bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, uint64_t entrySize, uint8_t* scratchSpace, + size_t nameSpaceLength, const char* name, size_t nameLength, + uint64_t entrySize, uint8_t* scratchSpace, size_t scratchSpaceSize) { uint8_t* additionalDataCbor = scratchSpace; const size_t additionalDataCborBufSize = scratchSpaceSize; @@ -233,9 +275,9 @@ bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControl // 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)) { + nameSpace, nameSpaceLength, name, nameLength, + additionalDataCbor, additionalDataCborBufSize, + &additionalDataCborSize, ctx->additionalDataSha256)) { return false; } @@ -244,7 +286,7 @@ bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControl ctx->curNamespaceNumProcessed = 0; // Opens the main map: { * Namespace => [ + Entry ] } eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts); - eicCborAppendString(&ctx->cbor, nameSpace); + eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength); // Opens the per-namespace array: [ + Entry ] eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]); } @@ -252,37 +294,39 @@ bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControl if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) { ctx->curNamespace += 1; ctx->curNamespaceNumProcessed = 0; - eicCborAppendString(&ctx->cbor, nameSpace); + eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength); // 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); + eicCborAppendStringZ(&ctx->cbor, "name"); + eicCborAppendString(&ctx->cbor, name, nameLength); ctx->curEntrySize = entrySize; ctx->curEntryNumBytesReceived = 0; - eicCborAppendString(&ctx->cbor, "value"); + eicCborAppendStringZ(&ctx->cbor, "value"); ctx->curNamespaceNumProcessed += 1; return true; } -bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds, +bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, const uint8_t* content, size_t contentSize, + size_t nameSpaceLength, const char* name, size_t nameLength, + 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, + nameSpace, nameSpaceLength, name, nameLength, + additionalDataCbor, additionalDataCborBufSize, + &additionalDataCborSize, calculatedSha256)) { return false; } @@ -305,7 +349,7 @@ bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControl // If done with this entry, close the map ctx->curEntryNumBytesReceived += contentSize; if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) { - eicCborAppendString(&ctx->cbor, "accessControlProfiles"); + eicCborAppendStringZ(&ctx->cbor, "accessControlProfiles"); eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds); for (size_t n = 0; n < numAccessControlProfileIds; n++) { eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]); @@ -337,6 +381,7 @@ bool eicProvisioningFinishAddingEntries( } bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType, + size_t docTypeLength, uint8_t* encryptedCredentialKeys, size_t* encryptedCredentialKeysSize) { EicCbor cbor; @@ -367,7 +412,7 @@ bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* do if (!eicOpsEncryptAes128Gcm( eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size, // DocType is the additionalAuthenticatedData - (const uint8_t*)docType, eicStrLen(docType), encryptedCredentialKeys)) { + (const uint8_t*)docType, docTypeLength, encryptedCredentialKeys)) { eicDebug("Error encrypting CredentialKeys"); return false; } diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h index f064787b1b..2619bfc45e 100644 --- a/identity/aidl/default/libeic/EicProvisioning.h +++ b/identity/aidl/default/libeic/EicProvisioning.h @@ -31,6 +31,9 @@ extern "C" { #define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32 typedef struct { + // A non-zero number unique for this EicProvisioning instance + uint32_t id; + // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate() uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE]; @@ -65,31 +68,44 @@ typedef struct { bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential); bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType, - const uint8_t* encryptedCredentialKeys, + size_t docTypeLength, const uint8_t* encryptedCredentialKeys, size_t encryptedCredentialKeysSize); +bool eicProvisioningShutdown(EicProvisioning* ctx); + +bool eicProvisioningGetId(EicProvisioning* ctx, uint32_t* outId); + bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge, size_t challengeSize, const uint8_t* applicationId, - size_t applicationIdSize, uint8_t* publicKeyCert, + size_t applicationIdSize, const uint8_t* attestationKeyBlob, + size_t attestationKeyBlobSize, + const uint8_t* attestationKeyCert, + size_t attestationKeyCertSize, uint8_t* publicKeyCert, size_t* publicKeyCertSize); bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount, const int* entryCounts, size_t numEntryCounts, - const char* docType, + const char* docType, size_t docTypeLength, size_t expectedProofOfProvisioningingSize); +// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to +// avoid allocating stack space. +// 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]); + bool userAuthenticationRequired, + uint64_t timeoutMillis, uint64_t secureUserId, + uint8_t outMac[28], uint8_t* scratchSpace, + size_t scratchSpaceSize); // 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, +bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, uint64_t entrySize, uint8_t* scratchSpace, + size_t nameSpaceLength, const char* name, size_t nameLength, + uint64_t entrySize, uint8_t* scratchSpace, size_t scratchSpaceSize); // The outEncryptedContent array must be contentSize + 28 bytes long. @@ -97,9 +113,10 @@ bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControl // 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, +bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds, const char* nameSpace, - const char* name, const uint8_t* content, size_t contentSize, + size_t nameSpaceLength, const char* name, size_t nameLength, + const uint8_t* content, size_t contentSize, uint8_t* outEncryptedContent, uint8_t* scratchSpace, size_t scratchSpaceSize); @@ -128,6 +145,7 @@ bool eicProvisioningFinishAddingEntries( // |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes. // bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType, + size_t docTypeLength, uint8_t* encryptedCredentialKeys, size_t* encryptedCredentialKeysSize); diff --git a/identity/aidl/default/libeic/EicSession.c b/identity/aidl/default/libeic/EicSession.c new file mode 100644 index 0000000000..d0c7a0d77e --- /dev/null +++ b/identity/aidl/default/libeic/EicSession.c @@ -0,0 +1,120 @@ +/* + * 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 <inttypes.h> + +#include "EicCommon.h" +#include "EicSession.h" + +// Global used for assigning ids for session objects. +// +static uint32_t gSessionLastIdAssigned = 0; + +// The current session object or NULL if never initialized or if it has been shut down. +// +static EicSession* gSessionCurrent = NULL; + +EicSession* eicSessionGetForId(uint32_t sessionId) { + if (gSessionCurrent != NULL && gSessionCurrent->id == sessionId) { + return gSessionCurrent; + } + return NULL; +} + +bool eicSessionInit(EicSession* ctx) { + eicMemSet(ctx, '\0', sizeof(EicSession)); + + if (!eicNextId(&gSessionLastIdAssigned)) { + eicDebug("Error getting id for object"); + return false; + } + ctx->id = gSessionLastIdAssigned; + + do { + if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(ctx->authChallenge))) { + eicDebug("Failed generating random challenge"); + return false; + } + } while (ctx->authChallenge == EIC_KM_AUTH_CHALLENGE_UNSET); + + if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ctx->ephemeralPublicKey)) { + eicDebug("Error creating ephemeral key-pair"); + return false; + } + + gSessionCurrent = ctx; + eicDebug("Initialized session with id %" PRIu32, ctx->id); + return true; +} + +bool eicSessionShutdown(EicSession* ctx) { + if (ctx->id == 0) { + eicDebug("Trying to shut down session with id 0"); + return false; + } + eicDebug("Shut down session with id %" PRIu32, ctx->id); + eicMemSet(ctx, '\0', sizeof(EicSession)); + gSessionCurrent = NULL; + return true; +} + +bool eicSessionGetId(EicSession* ctx, uint32_t* outId) { + *outId = ctx->id; + return true; +} + +bool eicSessionGetAuthChallenge(EicSession* ctx, uint64_t* outAuthChallenge) { + *outAuthChallenge = ctx->authChallenge; + return true; +} + +bool eicSessionGetEphemeralKeyPair(EicSession* ctx, + uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) { + eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey, EIC_P256_PRIV_KEY_SIZE); + return true; +} + +bool eicSessionSetReaderEphemeralPublicKey( + EicSession* ctx, const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE]) { + eicMemCpy(ctx->readerEphemeralPublicKey, readerEphemeralPublicKey, EIC_P256_PUB_KEY_SIZE); + return true; +} + +bool eicSessionSetSessionTranscript(EicSession* ctx, const uint8_t* sessionTranscript, + size_t sessionTranscriptSize) { + // Only accept the SessionTranscript if X and Y from the ephemeral key + // we created is somewhere in SessionTranscript... + // + if (eicMemMem(sessionTranscript, sessionTranscriptSize, ctx->ephemeralPublicKey, + EIC_P256_PUB_KEY_SIZE / 2) == NULL) { + eicDebug("Error finding X from ephemeralPublicKey in sessionTranscript"); + return false; + } + if (eicMemMem(sessionTranscript, sessionTranscriptSize, + ctx->ephemeralPublicKey + EIC_P256_PUB_KEY_SIZE / 2, + EIC_P256_PUB_KEY_SIZE / 2) == NULL) { + eicDebug("Error finding Y from ephemeralPublicKey in sessionTranscript"); + return false; + } + + // To save space we only store the SHA-256 of SessionTranscript + // + EicSha256Ctx shaCtx; + eicOpsSha256Init(&shaCtx); + eicOpsSha256Update(&shaCtx, sessionTranscript, sessionTranscriptSize); + eicOpsSha256Final(&shaCtx, ctx->sessionTranscriptSha256); + return true; +} diff --git a/identity/aidl/default/libeic/EicSession.h b/identity/aidl/default/libeic/EicSession.h new file mode 100644 index 0000000000..0303dae1c3 --- /dev/null +++ b/identity/aidl/default/libeic/EicSession.h @@ -0,0 +1,73 @@ +/* + * Copyright 2021, 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_SESSION_H +#define ANDROID_HARDWARE_IDENTITY_EIC_SESSION_H + +#include "EicOps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // A non-zero number unique for this EicSession instance + uint32_t id; + + // The challenge generated at construction time by eicSessionInit(). + uint64_t authChallenge; + + uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]; + uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE]; + + uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE]; + + uint8_t sessionTranscriptSha256[EIC_SHA256_DIGEST_SIZE]; + +} EicSession; + +bool eicSessionInit(EicSession* ctx); + +bool eicSessionShutdown(EicSession* ctx); + +bool eicSessionGetId(EicSession* ctx, uint32_t* outId); + +bool eicSessionGetAuthChallenge(EicSession* ctx, uint64_t* outAuthChallenge); + +bool eicSessionGetEphemeralKeyPair(EicSession* ctx, + uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]); + +bool eicSessionSetReaderEphemeralPublicKey( + EicSession* ctx, const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE]); + +bool eicSessionSetSessionTranscript(EicSession* ctx, const uint8_t* sessionTranscript, + size_t sessionTranscriptSize); + +// Looks up an active session with the given id. +// +// Returns NULL if no active session with the given id is found. +// +EicSession* eicSessionGetForId(uint32_t sessionId); + +#ifdef __cplusplus +} +#endif + +#endif // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H diff --git a/identity/aidl/default/libeic/libeic.h b/identity/aidl/default/libeic/libeic.h index 88abef808f..d89fc9a0e3 100644 --- a/identity/aidl/default/libeic/libeic.h +++ b/identity/aidl/default/libeic/libeic.h @@ -27,9 +27,11 @@ extern "C" { */ #define EIC_INSIDE_LIBEIC_H #include "EicCbor.h" +#include "EicCommon.h" #include "EicOps.h" #include "EicPresentation.h" #include "EicProvisioning.h" +#include "EicSession.h" #undef EIC_INSIDE_LIBEIC_H #ifdef __cplusplus |