mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "identity: Add multi-document presentation support." am: 2e182eaf1a am: 0e7c06f8f4
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1824772 Change-Id: Ife5c44ab279dfb1a0d496db39630d6b29c1555f8
This commit is contained in:
@@ -292,7 +292,7 @@
|
||||
</hal>
|
||||
<hal format="aidl" optional="true">
|
||||
<name>android.hardware.identity</name>
|
||||
<version>1-3</version>
|
||||
<version>1-4</version>
|
||||
<interface>
|
||||
<name>IIdentityCredentialStore</name>
|
||||
<instance>default</instance>
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -36,6 +37,7 @@ interface IIdentityCredentialStore {
|
||||
android.hardware.identity.HardwareInformation getHardwareInformation();
|
||||
android.hardware.identity.IWritableIdentityCredential createCredential(in @utf8InCpp String docType, in boolean testCredential);
|
||||
android.hardware.identity.IIdentityCredential getCredential(in android.hardware.identity.CipherSuite cipherSuite, in byte[] credentialData);
|
||||
android.hardware.identity.IPresentationSession createPresentationSession(in android.hardware.identity.CipherSuite cipherSuite);
|
||||
const int STATUS_OK = 0;
|
||||
const int STATUS_FAILED = 1;
|
||||
const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.identity;
|
||||
@VintfStability
|
||||
interface IPresentationSession {
|
||||
byte[] getEphemeralKeyPair();
|
||||
long getAuthChallenge();
|
||||
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
||||
void setSessionTranscript(in byte[] sessionTranscript);
|
||||
android.hardware.identity.IIdentityCredential getCredential(in byte[] credentialData);
|
||||
}
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* 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.
|
||||
*////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.identity.Certificate;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
import android.hardware.identity.RequestNamespace;
|
||||
import android.hardware.identity.SecureAccessControlProfile;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
import android.hardware.keymaster.HardwareAuthToken;
|
||||
import android.hardware.keymaster.VerificationToken;
|
||||
|
||||
@@ -44,6 +44,9 @@ interface IIdentityCredential {
|
||||
* This method was deprecated in API version 3 because there's no challenge so freshness
|
||||
* can't be checked. Use deleteCredentalWithChallenge() instead.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @return a COSE_Sign1 signature described above
|
||||
* @deprecated use deleteCredentalWithChallenge() instead.
|
||||
*/
|
||||
@@ -60,6 +63,9 @@ interface IIdentityCredential {
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @return the private key, in DER format as specified in RFC 5915.
|
||||
*/
|
||||
byte[] createEphemeralKeyPair();
|
||||
@@ -70,6 +76,9 @@ interface IIdentityCredential {
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @param publicKey contains the reader's ephemeral public key, in uncompressed
|
||||
* form (e.g. 0x04 || X || Y).
|
||||
*/
|
||||
@@ -83,6 +92,9 @@ interface IIdentityCredential {
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned. If user authentication is not needed, this method may not be called.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @return challenge, a non-zero number.
|
||||
*/
|
||||
long createAuthChallenge();
|
||||
@@ -371,6 +383,9 @@ interface IIdentityCredential {
|
||||
* This CBOR enables an issuer to determine the exact state of the credential it
|
||||
* returns issuer-signed data for.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
|
||||
* where signingKey is an EC private key in uncompressed form. That is, the returned
|
||||
* blob is an encrypted copy of the newly-generated private signing key.
|
||||
@@ -420,6 +435,9 @@ interface IIdentityCredential {
|
||||
*
|
||||
* This method was introduced in API version 3.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
|
||||
* and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
|
||||
* @return a COSE_Sign1 signature described above.
|
||||
@@ -442,6 +460,9 @@ interface IIdentityCredential {
|
||||
*
|
||||
* This method was introduced in API version 3.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
|
||||
* and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
|
||||
* @return a COSE_Sign1 signature described above.
|
||||
@@ -456,6 +477,9 @@ interface IIdentityCredential {
|
||||
*
|
||||
* This method was introduced in API version 3.
|
||||
*
|
||||
* If the method is called on an instance obtained via IPresentationSession.getCredential(),
|
||||
* STATUS_FAILED must be returned.
|
||||
*
|
||||
* @return an IWritableIdentityCredential
|
||||
*/
|
||||
IWritableIdentityCredential updateCredential();
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.identity.IIdentityCredential;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
import android.hardware.identity.HardwareInformation;
|
||||
import android.hardware.identity.CipherSuite;
|
||||
import android.hardware.identity.HardwareInformation;
|
||||
import android.hardware.identity.IIdentityCredential;
|
||||
import android.hardware.identity.IPresentationSession;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
|
||||
/**
|
||||
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
|
||||
@@ -105,7 +106,7 @@ import android.hardware.identity.CipherSuite;
|
||||
* STATUS_* integers defined in this interface. Each method states which status can be returned
|
||||
* and under which circumstances.
|
||||
*
|
||||
* The API described here is API version 3 which corresponds to feature version 202101
|
||||
* The API described here is API version 4 which corresponds to feature version 202201
|
||||
* of the android.security.identity Framework API. An XML file declaring the feature
|
||||
* android.hardware.identity_credential (or android.hardware.identity_credential.direct_access
|
||||
* if implementing the Direct Access HAL) should be included declaring this feature version.
|
||||
@@ -241,4 +242,25 @@ interface IIdentityCredentialStore {
|
||||
* @return an IIdentityCredential interface that provides operations on the Credential.
|
||||
*/
|
||||
IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
|
||||
|
||||
/**
|
||||
* createPresentationSession creates IPresentationSession interface which can be used to
|
||||
* present one or more credentials to a remote verifier device.
|
||||
*
|
||||
* The cipher suite used to communicate with the remote verifier must be specified. Currently
|
||||
* only a single cipher-suite is supported. Support for other cipher suites may be added in a
|
||||
* future version of this HAL. If the requested cipher suite is not support the call fails
|
||||
* with STATUS_CIPHER_SUITE_NOT_SUPPORTED.
|
||||
*
|
||||
* In this version of the HAL, implementations are only required to support a single session
|
||||
* being active. In a future version, implementations may be required to support multiple
|
||||
* presentation sessions being active at the same time.
|
||||
*
|
||||
* This method was introduced in API version 4.
|
||||
*
|
||||
* @param cipherSuite The cipher suite to use.
|
||||
*
|
||||
* @return an IPresentationSession interface.
|
||||
*/
|
||||
IPresentationSession createPresentationSession(in CipherSuite cipherSuite);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.identity.CipherSuite;
|
||||
import android.hardware.identity.IIdentityCredential;
|
||||
|
||||
/**
|
||||
* An interface to present multiple credentials in the same session.
|
||||
*
|
||||
* This interface was introduced in API version 4.
|
||||
*
|
||||
*/
|
||||
@VintfStability
|
||||
interface IPresentationSession {
|
||||
/**
|
||||
* Gets the ephemeral EC key pair to be used in establishing a secure session with a reader.
|
||||
* This method returns the private key so the caller can perform an ECDH key agreement operation
|
||||
* with the reader. The reason for generating the key pair in the secure environment is so that
|
||||
* the secure environment knows what public key to expect to find in the session transcript
|
||||
* when presenting credentials.
|
||||
*
|
||||
* The generated key matches the selected cipher suite of the presentation session (e.g. EC
|
||||
* key using the P-256 curve).
|
||||
*
|
||||
* @return the private key, in DER format as specified in RFC 5915.
|
||||
*/
|
||||
byte[] getEphemeralKeyPair();
|
||||
|
||||
/**
|
||||
* Gets the challenge value to be used for proving successful user authentication. This
|
||||
* is to be included in the authToken passed to the IIdentityCredential.startRetrieval()
|
||||
* method and the verificationToken passed to the IIdentityCredential.setVerificationToken()
|
||||
* method.
|
||||
*
|
||||
* @return challenge, a non-zero number.
|
||||
*/
|
||||
long getAuthChallenge();
|
||||
|
||||
/**
|
||||
* Sets the public part of the reader's ephemeral key pair to be used to complete
|
||||
* an ECDH key agreement for the session.
|
||||
*
|
||||
* The curve of the key must match the curve for the key returned by getEphemeralKeyPair().
|
||||
*
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* must be returned.
|
||||
*
|
||||
* @param publicKey contains the reader's ephemeral public key, in uncompressed
|
||||
* form (e.g. 0x04 || X || Y).
|
||||
*/
|
||||
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
||||
|
||||
/**
|
||||
* Sets the session transcript for the session.
|
||||
*
|
||||
* This can be empty but if it's non-empty it must be valid CBOR.
|
||||
*
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* must be returned.
|
||||
*
|
||||
* @param sessionTrancsript the session transcript.
|
||||
*/
|
||||
void setSessionTranscript(in byte[] sessionTranscript);
|
||||
|
||||
/**
|
||||
* getCredential() retrieves an IIdentityCredential interface for presentation in the
|
||||
* current presentation session.
|
||||
*
|
||||
* On the returned instance only the methods startRetrieval(), startRetrieveEntryValue(),
|
||||
* retrieveEntryValue(), finishRetrieval(), setRequestedNamespaces(), setVerificationToken()
|
||||
* may be called. Other methods will fail with STATUS_FAILED.
|
||||
*
|
||||
* The implementation is expected to get the session transcript, ephemeral key, reader
|
||||
* ephemeral key, and auth challenge from this instance.
|
||||
*
|
||||
* @param credentialData is a CBOR-encoded structure containing metadata about the credential
|
||||
* and an encrypted byte array that contains data used to secure the credential. See the
|
||||
* return argument of the same name in IWritableIdentityCredential.finishAddingEntries().
|
||||
*
|
||||
* Note that the format of credentialData may depend on the feature version.
|
||||
* Implementations must support credentialData created by an earlier feature version.
|
||||
*
|
||||
* @return an IIdentityCredential interface that provides operations on the Credential.
|
||||
*/
|
||||
IIdentityCredential getCredential(in byte[] credentialData);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ cc_library_static {
|
||||
srcs: [
|
||||
"common/IdentityCredential.cpp",
|
||||
"common/IdentityCredentialStore.cpp",
|
||||
"common/PresentationSession.cpp",
|
||||
"common/WritableIdentityCredential.cpp",
|
||||
],
|
||||
export_include_dirs: [
|
||||
@@ -39,8 +40,8 @@ cc_library_static {
|
||||
"libsoft_attestation_cert",
|
||||
"libpuresoftkeymasterdevice",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity-V3-ndk",
|
||||
"android.hardware.keymaster-V3-ndk",
|
||||
"android.hardware.identity-V4-ndk",
|
||||
"android.hardware.keymaster-V4-ndk",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ cc_library_static {
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"libeic/EicCbor.c",
|
||||
"libeic/EicSession.c",
|
||||
"libeic/EicPresentation.c",
|
||||
"libeic/EicProvisioning.c",
|
||||
"EicOpsImpl.cc",
|
||||
@@ -100,8 +102,8 @@ cc_binary {
|
||||
"libsoft_attestation_cert",
|
||||
"libpuresoftkeymasterdevice",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity-V3-ndk",
|
||||
"android.hardware.keymaster-V3-ndk",
|
||||
"android.hardware.identity-V4-ndk",
|
||||
"android.hardware.keymaster-V4-ndk",
|
||||
"android.hardware.identity-libeic-hal-common",
|
||||
"android.hardware.identity-libeic-library",
|
||||
],
|
||||
|
||||
@@ -20,9 +20,13 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
@@ -63,6 +67,11 @@ size_t eicStrLen(const char* s) {
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
void* eicMemMem(const uint8_t* haystack, size_t haystackLen, const uint8_t* needle,
|
||||
size_t needleLen) {
|
||||
return memmem(haystack, haystackLen, needle, needleLen);
|
||||
}
|
||||
|
||||
int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) {
|
||||
return CRYPTO_memcmp(s1, s2, n);
|
||||
}
|
||||
@@ -117,6 +126,25 @@ bool eicOpsRandom(uint8_t* buf, size_t numBytes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eicNextId(uint32_t* id) {
|
||||
uint32_t oldId = *id;
|
||||
uint32_t newId = 0;
|
||||
|
||||
do {
|
||||
union {
|
||||
uint8_t value8;
|
||||
uint32_t value32;
|
||||
} value;
|
||||
if (!eicOpsRandom(&value.value8, sizeof(value))) {
|
||||
return false;
|
||||
}
|
||||
newId = value.value32;
|
||||
} while (newId == oldId && newId == 0);
|
||||
|
||||
*id = newId;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eicOpsEncryptAes128Gcm(
|
||||
const uint8_t* key, // Must be 16 bytes
|
||||
const uint8_t* nonce, // Must be 12 bytes
|
||||
|
||||
@@ -66,7 +66,8 @@ TEST(EicTest, AccessControlIsEnforced) {
|
||||
// Then present data from it...
|
||||
//
|
||||
FakeSecureHardwarePresentationProxy presentationProxy;
|
||||
ASSERT_TRUE(presentationProxy.initialize(isTestCredential, docType, credData.value()));
|
||||
ASSERT_TRUE(presentationProxy.initialize(0 /* sessionId */, isTestCredential, docType,
|
||||
credData.value()));
|
||||
AccessCheckResult res =
|
||||
presentationProxy.startRetrieveEntryValue(nameSpace, name, 1, content.size(), acpIds);
|
||||
ASSERT_EQ(res, AccessCheckResult::kNoAccessControlProfiles);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
@@ -52,38 +53,110 @@ namespace android::hardware::identity {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FakeSecureHardwareProvisioningProxy::FakeSecureHardwareProvisioningProxy() {}
|
||||
// The singleton EicProvisioning object used everywhere.
|
||||
//
|
||||
EicProvisioning FakeSecureHardwareProvisioningProxy::ctx_;
|
||||
|
||||
FakeSecureHardwareProvisioningProxy::~FakeSecureHardwareProvisioningProxy() {}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::shutdown() {
|
||||
LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
|
||||
return true;
|
||||
FakeSecureHardwareProvisioningProxy::~FakeSecureHardwareProvisioningProxy() {
|
||||
if (id_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) {
|
||||
LOG(INFO) << "FakeSecureHardwareProvisioningProxy created, sizeof(EicProvisioning): "
|
||||
<< sizeof(EicProvisioning);
|
||||
return eicProvisioningInit(&ctx_, testCredential);
|
||||
if (id_ != 0) {
|
||||
LOG(WARNING) << "Proxy is already initialized";
|
||||
return false;
|
||||
}
|
||||
bool initialized = eicProvisioningInit(&ctx_, testCredential);
|
||||
if (!initialized) {
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "Error getting id";
|
||||
return false;
|
||||
}
|
||||
id_ = id.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::initializeForUpdate(
|
||||
bool testCredential, string docType, vector<uint8_t> encryptedCredentialKeys) {
|
||||
return eicProvisioningInitForUpdate(&ctx_, testCredential, docType.c_str(),
|
||||
docType.size(),
|
||||
encryptedCredentialKeys.data(),
|
||||
encryptedCredentialKeys.size());
|
||||
bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) {
|
||||
if (id_ != 0) {
|
||||
LOG(WARNING) << "Proxy is already initialized";
|
||||
return false;
|
||||
}
|
||||
bool initialized = eicProvisioningInitForUpdate(&ctx_, testCredential, docType.c_str(),
|
||||
docType.size(), encryptedCredentialKeys.data(),
|
||||
encryptedCredentialKeys.size());
|
||||
if (!initialized) {
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "Error getting id";
|
||||
return false;
|
||||
}
|
||||
id_ = id.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<uint32_t> FakeSecureHardwareProvisioningProxy::getId() {
|
||||
uint32_t id;
|
||||
if (!eicProvisioningGetId(&ctx_, &id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::validateId(const string& callerName) {
|
||||
if (id_ == 0) {
|
||||
LOG(WARNING) << "FakeSecureHardwareProvisioningProxy::" << callerName
|
||||
<< ": While validating expected id is 0";
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "FakeSecureHardwareProvisioningProxy::" << callerName
|
||||
<< ": Error getting id for validating";
|
||||
return false;
|
||||
}
|
||||
if (id.value() != id_) {
|
||||
LOG(WARNING) << "FakeSecureHardwareProvisioningProxy::" << callerName
|
||||
<< ": While validating expected id " << id_ << " but got " << id.value();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::shutdown() {
|
||||
bool validated = validateId(__func__);
|
||||
id_ = 0;
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
if (!eicProvisioningShutdown(&ctx_)) {
|
||||
LOG(INFO) << "Error shutting down provisioning";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns public key certificate.
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint8_t publicKeyCert[4096];
|
||||
size_t publicKeyCertSize = sizeof publicKeyCert;
|
||||
if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
|
||||
applicationId.data(), applicationId.size(),
|
||||
publicKeyCert, &publicKeyCertSize)) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
vector<uint8_t> pubKeyCert(publicKeyCertSize);
|
||||
memcpy(pubKeyCert.data(), publicKeyCert, publicKeyCertSize);
|
||||
@@ -91,8 +164,11 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialK
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::startPersonalization(
|
||||
int accessControlProfileCount, vector<int> entryCounts, const string& docType,
|
||||
int accessControlProfileCount, const vector<int>& entryCounts, const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eicProvisioningStartPersonalization(&ctx_, accessControlProfileCount,
|
||||
entryCounts.data(),
|
||||
@@ -108,13 +184,17 @@ bool FakeSecureHardwareProvisioningProxy::startPersonalization(
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addAccessControlProfile(
|
||||
int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
|
||||
uint64_t timeoutMillis, uint64_t secureUserId) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> mac(28);
|
||||
uint8_t scratchSpace[512];
|
||||
if (!eicProvisioningAddAccessControlProfile(
|
||||
&ctx_, id, readerCertificate.data(), readerCertificate.size(),
|
||||
userAuthenticationRequired, timeoutMillis, secureUserId, mac.data(),
|
||||
scratchSpace, sizeof(scratchSpace))) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return mac;
|
||||
}
|
||||
@@ -122,6 +202,10 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addAccessControlP
|
||||
bool FakeSecureHardwareProvisioningProxy::beginAddEntry(const vector<int>& accessControlProfileIds,
|
||||
const string& nameSpace, const string& name,
|
||||
uint64_t entrySize) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t scratchSpace[512];
|
||||
vector<uint8_t> uint8AccessControlProfileIds;
|
||||
for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
|
||||
@@ -138,6 +222,10 @@ bool FakeSecureHardwareProvisioningProxy::beginAddEntry(const vector<int>& acces
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addEntryValue(
|
||||
const vector<int>& accessControlProfileIds, const string& nameSpace, const string& name,
|
||||
const vector<uint8_t>& content) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> eicEncryptedContent;
|
||||
uint8_t scratchSpace[512];
|
||||
vector<uint8_t> uint8AccessControlProfileIds;
|
||||
@@ -150,16 +238,20 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addEntryValue(
|
||||
&ctx_, uint8AccessControlProfileIds.data(), uint8AccessControlProfileIds.size(),
|
||||
nameSpace.c_str(), nameSpace.size(), name.c_str(), name.size(), content.data(),
|
||||
content.size(), eicEncryptedContent.data(), scratchSpace, sizeof(scratchSpace))) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return eicEncryptedContent;
|
||||
}
|
||||
|
||||
// Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntries() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
|
||||
if (!eicProvisioningFinishAddingEntries(&ctx_, signatureOfToBeSigned.data())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return signatureOfToBeSigned;
|
||||
}
|
||||
@@ -167,11 +259,15 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntri
|
||||
// Returns encryptedCredentialKeys.
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
|
||||
const string& docType) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> encryptedCredentialKeys(116);
|
||||
size_t size = encryptedCredentialKeys.size();
|
||||
if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(), docType.size(),
|
||||
encryptedCredentialKeys.data(), &size)) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
encryptedCredentialKeys.resize(size);
|
||||
return encryptedCredentialKeys;
|
||||
@@ -179,21 +275,200 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredenti
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FakeSecureHardwarePresentationProxy::FakeSecureHardwarePresentationProxy() {}
|
||||
// The singleton EicSession object used everywhere.
|
||||
//
|
||||
EicSession FakeSecureHardwareSessionProxy::ctx_;
|
||||
|
||||
FakeSecureHardwarePresentationProxy::~FakeSecureHardwarePresentationProxy() {}
|
||||
FakeSecureHardwareSessionProxy::~FakeSecureHardwareSessionProxy() {
|
||||
if (id_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string docType,
|
||||
vector<uint8_t> encryptedCredentialKeys) {
|
||||
LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
|
||||
<< sizeof(EicPresentation);
|
||||
return eicPresentationInit(&ctx_, testCredential, docType.c_str(), docType.size(),
|
||||
encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
|
||||
bool FakeSecureHardwareSessionProxy::initialize() {
|
||||
if (id_ != 0) {
|
||||
LOG(WARNING) << "Proxy is already initialized";
|
||||
return false;
|
||||
}
|
||||
bool initialized = eicSessionInit(&ctx_);
|
||||
if (!initialized) {
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "Error getting id";
|
||||
return false;
|
||||
}
|
||||
id_ = id.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<uint32_t> FakeSecureHardwareSessionProxy::getId() {
|
||||
uint32_t id;
|
||||
if (!eicSessionGetId(&ctx_, &id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareSessionProxy::shutdown() {
|
||||
bool validated = validateId(__func__);
|
||||
id_ = 0;
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
if (!eicSessionShutdown(&ctx_)) {
|
||||
LOG(INFO) << "Error shutting down session";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareSessionProxy::validateId(const string& callerName) {
|
||||
if (id_ == 0) {
|
||||
LOG(WARNING) << "FakeSecureHardwareSessionProxy::" << callerName
|
||||
<< ": While validating expected id is 0";
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "FakeSecureHardwareSessionProxy::" << callerName
|
||||
<< ": Error getting id for validating";
|
||||
return false;
|
||||
}
|
||||
if (id.value() != id_) {
|
||||
LOG(WARNING) << "FakeSecureHardwareSessionProxy::" << callerName
|
||||
<< ": While validating expected id " << id_ << " but got " << id.value();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<uint64_t> FakeSecureHardwareSessionProxy::getAuthChallenge() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint64_t authChallenge;
|
||||
if (!eicSessionGetAuthChallenge(&ctx_, &authChallenge)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return authChallenge;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> FakeSecureHardwareSessionProxy::getEphemeralKeyPair() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
|
||||
if (!eicSessionGetEphemeralKeyPair(&ctx_, priv.data())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return priv;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareSessionProxy::setReaderEphemeralPublicKey(
|
||||
const vector<uint8_t>& readerEphemeralPublicKey) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicSessionSetReaderEphemeralPublicKey(&ctx_, readerEphemeralPublicKey.data());
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareSessionProxy::setSessionTranscript(
|
||||
const vector<uint8_t>& sessionTranscript) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicSessionSetSessionTranscript(&ctx_, sessionTranscript.data(),
|
||||
sessionTranscript.size());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// The singleton EicPresentation object used everywhere.
|
||||
//
|
||||
EicPresentation FakeSecureHardwarePresentationProxy::ctx_;
|
||||
|
||||
FakeSecureHardwarePresentationProxy::~FakeSecureHardwarePresentationProxy() {
|
||||
if (id_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::initialize(
|
||||
uint32_t sessionId, bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) {
|
||||
if (id_ != 0) {
|
||||
LOG(WARNING) << "Proxy is already initialized";
|
||||
return false;
|
||||
}
|
||||
bool initialized =
|
||||
eicPresentationInit(&ctx_, sessionId, testCredential, docType.c_str(), docType.size(),
|
||||
encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
|
||||
if (!initialized) {
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "Error getting id";
|
||||
return false;
|
||||
}
|
||||
id_ = id.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<uint32_t> FakeSecureHardwarePresentationProxy::getId() {
|
||||
uint32_t id;
|
||||
if (!eicPresentationGetId(&ctx_, &id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::validateId(const string& callerName) {
|
||||
if (id_ == 0) {
|
||||
LOG(WARNING) << "FakeSecureHardwarePresentationProxy::" << callerName
|
||||
<< ": While validating expected id is 0";
|
||||
return false;
|
||||
}
|
||||
optional<uint32_t> id = getId();
|
||||
if (!id) {
|
||||
LOG(WARNING) << "FakeSecureHardwarePresentationProxy::" << callerName
|
||||
<< ": Error getting id for validating";
|
||||
return false;
|
||||
}
|
||||
if (id.value() != id_) {
|
||||
LOG(WARNING) << "FakeSecureHardwarePresentationProxy::" << callerName
|
||||
<< ": While validating expected id " << id_ << " but got " << id.value();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::shutdown() {
|
||||
bool validated = validateId(__func__);
|
||||
id_ = 0;
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
if (!eicPresentationShutdown(&ctx_)) {
|
||||
LOG(INFO) << "Error shutting down presentation";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
|
||||
optional<pair<vector<uint8_t>, vector<uint8_t>>>
|
||||
FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time_t now) {
|
||||
FakeSecureHardwarePresentationProxy::generateSigningKeyPair(const string& docType, time_t now) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint8_t publicKeyCert[512];
|
||||
size_t publicKeyCertSize = sizeof(publicKeyCert);
|
||||
vector<uint8_t> signingKeyBlob(60);
|
||||
@@ -201,7 +476,7 @@ FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time
|
||||
if (!eicPresentationGenerateSigningKeyPair(&ctx_, docType.c_str(), docType.size(), now,
|
||||
publicKeyCert, &publicKeyCertSize,
|
||||
signingKeyBlob.data())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> cert;
|
||||
@@ -213,33 +488,44 @@ FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time
|
||||
|
||||
// Returns private key
|
||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::createEphemeralKeyPair() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
|
||||
if (!eicPresentationCreateEphemeralKeyPair(&ctx_, priv.data())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return priv;
|
||||
}
|
||||
|
||||
optional<uint64_t> FakeSecureHardwarePresentationProxy::createAuthChallenge() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint64_t challenge;
|
||||
if (!eicPresentationCreateAuthChallenge(&ctx_, &challenge)) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return challenge;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::shutdown() {
|
||||
LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::pushReaderCert(const vector<uint8_t>& certX509) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicPresentationPushReaderCert(&ctx_, certX509.data(), certX509.size());
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::validateRequestMessage(
|
||||
const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& requestMessage,
|
||||
int coseSignAlg, const vector<uint8_t>& readerSignatureOfToBeSigned) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicPresentationValidateRequestMessage(
|
||||
&ctx_, sessionTranscript.data(), sessionTranscript.size(), requestMessage.data(),
|
||||
requestMessage.size(), coseSignAlg, readerSignatureOfToBeSigned.data(),
|
||||
@@ -251,6 +537,10 @@ bool FakeSecureHardwarePresentationProxy::setAuthToken(
|
||||
int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
|
||||
uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
|
||||
int verificationTokenSecurityLevel, const vector<uint8_t>& verificationTokenMac) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicPresentationSetAuthToken(&ctx_, challenge, secureUserId, authenticatorId,
|
||||
hardwareAuthenticatorType, timeStamp, mac.data(), mac.size(),
|
||||
verificationTokenChallenge, verificationTokenTimestamp,
|
||||
@@ -261,6 +551,10 @@ bool FakeSecureHardwarePresentationProxy::setAuthToken(
|
||||
optional<bool> FakeSecureHardwarePresentationProxy::validateAccessControlProfile(
|
||||
int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
|
||||
int timeoutMillis, uint64_t secureUserId, const vector<uint8_t>& mac) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool accessGranted = false;
|
||||
uint8_t scratchSpace[512];
|
||||
if (!eicPresentationValidateAccessControlProfile(&ctx_, id, readerCertificate.data(),
|
||||
@@ -268,12 +562,16 @@ optional<bool> FakeSecureHardwarePresentationProxy::validateAccessControlProfile
|
||||
userAuthenticationRequired, timeoutMillis,
|
||||
secureUserId, mac.data(), &accessGranted,
|
||||
scratchSpace, sizeof(scratchSpace))) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return accessGranted;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwarePresentationProxy::startRetrieveEntries() {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return eicPresentationStartRetrieveEntries(&ctx_);
|
||||
}
|
||||
|
||||
@@ -281,6 +579,10 @@ bool FakeSecureHardwarePresentationProxy::calcMacKey(
|
||||
const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& readerEphemeralPublicKey,
|
||||
const vector<uint8_t>& signingKeyBlob, const string& docType,
|
||||
unsigned int numNamespacesWithValues, size_t expectedProofOfProvisioningSize) {
|
||||
if (!validateId(__func__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (signingKeyBlob.size() != 60) {
|
||||
eicDebug("Unexpected size %zd of signingKeyBlob, expected 60", signingKeyBlob.size());
|
||||
return false;
|
||||
@@ -294,6 +596,10 @@ bool FakeSecureHardwarePresentationProxy::calcMacKey(
|
||||
AccessCheckResult FakeSecureHardwarePresentationProxy::startRetrieveEntryValue(
|
||||
const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
|
||||
int32_t entrySize, const vector<int32_t>& accessControlProfileIds) {
|
||||
if (!validateId(__func__)) {
|
||||
return AccessCheckResult::kFailed;
|
||||
}
|
||||
|
||||
uint8_t scratchSpace[512];
|
||||
vector<uint8_t> uint8AccessControlProfileIds;
|
||||
for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
|
||||
@@ -324,6 +630,10 @@ AccessCheckResult FakeSecureHardwarePresentationProxy::startRetrieveEntryValue(
|
||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::retrieveEntryValue(
|
||||
const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
|
||||
const vector<int32_t>& accessControlProfileIds) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint8_t scratchSpace[512];
|
||||
vector<uint8_t> uint8AccessControlProfileIds;
|
||||
for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
|
||||
@@ -337,16 +647,20 @@ optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::retrieveEntryValu
|
||||
nameSpace.c_str(), nameSpace.size(), name.c_str(), name.size(),
|
||||
uint8AccessControlProfileIds.data(), uint8AccessControlProfileIds.size(),
|
||||
scratchSpace, sizeof(scratchSpace))) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval() {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> mac(32);
|
||||
size_t macSize = 32;
|
||||
if (!eicPresentationFinishRetrieval(&ctx_, mac.data(), &macSize)) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
mac.resize(macSize);
|
||||
return mac;
|
||||
@@ -355,11 +669,15 @@ optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval()
|
||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
|
||||
const string& docType, const vector<uint8_t>& challenge, bool includeChallenge,
|
||||
size_t proofOfDeletionCborSize) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
|
||||
if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), docType.size(), challenge.data(),
|
||||
challenge.size(), includeChallenge,
|
||||
proofOfDeletionCborSize, signatureOfToBeSigned.data())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return signatureOfToBeSigned;
|
||||
}
|
||||
@@ -367,11 +685,15 @@ optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
|
||||
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::proveOwnership(
|
||||
const string& docType, bool testCredential, const vector<uint8_t>& challenge,
|
||||
size_t proofOfOwnershipCborSize) {
|
||||
if (!validateId(__func__)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
|
||||
if (!eicPresentationProveOwnership(&ctx_, docType.c_str(), docType.size(), testCredential,
|
||||
challenge.data(), challenge.size(), proofOfOwnershipCborSize,
|
||||
signatureOfToBeSigned.data())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return signatureOfToBeSigned;
|
||||
}
|
||||
|
||||
@@ -27,21 +27,23 @@ namespace android::hardware::identity {
|
||||
//
|
||||
class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningProxy {
|
||||
public:
|
||||
FakeSecureHardwareProvisioningProxy();
|
||||
FakeSecureHardwareProvisioningProxy() = default;
|
||||
virtual ~FakeSecureHardwareProvisioningProxy();
|
||||
|
||||
bool initialize(bool testCredential) override;
|
||||
|
||||
bool initializeForUpdate(bool testCredential, string docType,
|
||||
vector<uint8_t> encryptedCredentialKeys) override;
|
||||
bool initializeForUpdate(bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) override;
|
||||
|
||||
bool shutdown() override;
|
||||
|
||||
optional<uint32_t> getId() override;
|
||||
|
||||
// Returns public key certificate.
|
||||
optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& applicationId) override;
|
||||
|
||||
bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
|
||||
bool startPersonalization(int accessControlProfileCount, const vector<int>& entryCounts,
|
||||
const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) override;
|
||||
|
||||
@@ -67,21 +69,81 @@ class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningPro
|
||||
optional<vector<uint8_t>> finishGetCredentialData(const string& docType) override;
|
||||
|
||||
protected:
|
||||
EicProvisioning ctx_;
|
||||
// See docs for id_.
|
||||
//
|
||||
bool validateId(const string& callerName);
|
||||
|
||||
// We use a singleton libeic object, shared by all proxy instances. This is to
|
||||
// properly simulate a situation where libeic is used on constrained hardware
|
||||
// with only enough RAM for a single instance of the libeic object.
|
||||
//
|
||||
static EicProvisioning ctx_;
|
||||
|
||||
// On the HAL side we keep track of the ID that was assigned to the libeic object
|
||||
// created in secure hardware. For every call into libeic we validate that this
|
||||
// identifier matches what is on the secure side. This is what the validateId()
|
||||
// method does.
|
||||
//
|
||||
uint32_t id_ = 0;
|
||||
};
|
||||
|
||||
// This implementation uses libEmbeddedIC in-process.
|
||||
//
|
||||
class FakeSecureHardwareSessionProxy : public SecureHardwareSessionProxy {
|
||||
public:
|
||||
FakeSecureHardwareSessionProxy() = default;
|
||||
virtual ~FakeSecureHardwareSessionProxy();
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool shutdown() override;
|
||||
|
||||
optional<uint32_t> getId() override;
|
||||
|
||||
optional<uint64_t> getAuthChallenge() override;
|
||||
|
||||
// Returns private key
|
||||
optional<vector<uint8_t>> getEphemeralKeyPair() override;
|
||||
|
||||
bool setReaderEphemeralPublicKey(const vector<uint8_t>& readerEphemeralPublicKey) override;
|
||||
|
||||
bool setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
|
||||
|
||||
protected:
|
||||
// See docs for id_.
|
||||
//
|
||||
bool validateId(const string& callerName);
|
||||
|
||||
// We use a singleton libeic object, shared by all proxy instances. This is to
|
||||
// properly simulate a situation where libeic is used on constrained hardware
|
||||
// with only enough RAM for a single instance of the libeic object.
|
||||
//
|
||||
static EicSession ctx_;
|
||||
|
||||
// On the HAL side we keep track of the ID that was assigned to the libeic object
|
||||
// created in secure hardware. For every call into libeic we validate that this
|
||||
// identifier matches what is on the secure side. This is what the validateId()
|
||||
// method does.
|
||||
//
|
||||
uint32_t id_ = 0;
|
||||
};
|
||||
|
||||
// This implementation uses libEmbeddedIC in-process.
|
||||
//
|
||||
class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationProxy {
|
||||
public:
|
||||
FakeSecureHardwarePresentationProxy();
|
||||
FakeSecureHardwarePresentationProxy() = default;
|
||||
virtual ~FakeSecureHardwarePresentationProxy();
|
||||
|
||||
bool initialize(bool testCredential, string docType,
|
||||
vector<uint8_t> encryptedCredentialKeys) override;
|
||||
bool initialize(uint32_t sessionId, bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) override;
|
||||
|
||||
bool shutdown() override;
|
||||
|
||||
optional<uint32_t> getId() override;
|
||||
|
||||
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
|
||||
optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
|
||||
optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(const string& docType,
|
||||
time_t now) override;
|
||||
|
||||
// Returns private key
|
||||
@@ -133,10 +195,23 @@ class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationPro
|
||||
const vector<uint8_t>& challenge,
|
||||
size_t proofOfOwnershipCborSize) override;
|
||||
|
||||
bool shutdown() override;
|
||||
|
||||
protected:
|
||||
EicPresentation ctx_;
|
||||
// See docs for id_.
|
||||
//
|
||||
bool validateId(const string& callerName);
|
||||
|
||||
// We use a singleton libeic object, shared by all proxy instances. This is to
|
||||
// properly simulate a situation where libeic is used on constrained hardware
|
||||
// with only enough RAM for a single instance of the libeic object.
|
||||
//
|
||||
static EicPresentation ctx_;
|
||||
|
||||
// On the HAL side we keep track of the ID that was assigned to the libeic object
|
||||
// created in secure hardware. For every call into libeic we validate that this
|
||||
// identifier matches what is on the secure side. This is what the validateId()
|
||||
// method does.
|
||||
//
|
||||
uint32_t id_ = 0;
|
||||
};
|
||||
|
||||
// Factory implementation.
|
||||
@@ -150,6 +225,10 @@ class FakeSecureHardwareProxyFactory : public SecureHardwareProxyFactory {
|
||||
return new FakeSecureHardwareProvisioningProxy();
|
||||
}
|
||||
|
||||
sp<SecureHardwareSessionProxy> createSessionProxy() override {
|
||||
return new FakeSecureHardwareSessionProxy();
|
||||
}
|
||||
|
||||
sp<SecureHardwarePresentationProxy> createPresentationProxy() override {
|
||||
return new FakeSecureHardwarePresentationProxy();
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<permissions>
|
||||
<feature name="android.hardware.identity_credential" version="202101" />
|
||||
<feature name="android.hardware.identity_credential" version="202201" />
|
||||
</permissions>
|
||||
|
||||
@@ -72,14 +72,38 @@ int IdentityCredential::initialize() {
|
||||
testCredential_ = testCredentialItem->value();
|
||||
|
||||
encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
|
||||
if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
|
||||
LOG(ERROR) << "hwProxy->initialize failed";
|
||||
return false;
|
||||
|
||||
// If in a session, delay the initialization of the proxy.
|
||||
//
|
||||
if (!session_) {
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
LOG(ERROR) << "Error initializing hw proxy";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return IIdentityCredentialStore::STATUS_OK;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::ensureHwProxy() {
|
||||
if (hwProxy_) {
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
hwProxy_ = hwProxyFactory_->createPresentationProxy();
|
||||
if (!hwProxy_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating hw proxy"));
|
||||
}
|
||||
uint64_t sessionId = session_ ? session_->getSessionId() : EIC_PRESENTATION_ID_UNSET;
|
||||
if (!hwProxy_->initialize(sessionId, testCredential_, docType_, encryptedCredentialKeys_)) {
|
||||
hwProxy_.clear();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error initializing hw proxy"));
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::deleteCredential(
|
||||
vector<uint8_t>* outProofOfDeletionSignature) {
|
||||
return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
|
||||
@@ -93,6 +117,14 @@ ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
|
||||
ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
|
||||
const vector<uint8_t>& challenge, bool includeChallenge,
|
||||
vector<uint8_t>* outProofOfDeletionSignature) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
if (challenge.size() > 32) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
|
||||
@@ -128,6 +160,14 @@ ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::proveOwnership(
|
||||
const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
if (challenge.size() > 32) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
|
||||
@@ -159,6 +199,14 @@ ndk::ScopedAStatus IdentityCredential::proveOwnership(
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
|
||||
if (!ephemeralPriv) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
@@ -186,11 +234,23 @@ ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* o
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
|
||||
const vector<uint8_t>& publicKey) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
readerPublicKey_ = publicKey;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
|
||||
if (!challenge) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
@@ -217,16 +277,22 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
|
||||
const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
|
||||
const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
|
||||
const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
|
||||
std::unique_ptr<cppbor::Item> sessionTranscriptItem;
|
||||
if (sessionTranscript.size() > 0) {
|
||||
auto [item, _, message] = cppbor::parse(sessionTranscript);
|
||||
if (item == nullptr) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"SessionTranscript contains invalid CBOR"));
|
||||
}
|
||||
sessionTranscriptItem = std::move(item);
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// If in a session, ensure the passed-in session transcript matches the
|
||||
// session transcript from the session.
|
||||
if (session_) {
|
||||
if (sessionTranscript != session_->getSessionTranscript()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
|
||||
"In a session and passed-in SessionTranscript doesn't match the one "
|
||||
"from the session"));
|
||||
}
|
||||
}
|
||||
|
||||
if (numStartRetrievalCalls_ > 0) {
|
||||
if (sessionTranscript_ != sessionTranscript) {
|
||||
LOG(ERROR) << "Session Transcript changed";
|
||||
@@ -390,32 +456,36 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this check to the TA
|
||||
#if 1
|
||||
// To prevent replay-attacks, we check that the public part of the ephemeral
|
||||
// key we previously created, is present in the DeviceEngagement part of
|
||||
// SessionTranscript as a COSE_Key, in uncompressed form.
|
||||
//
|
||||
// We do this by just searching for the X and Y coordinates.
|
||||
if (sessionTranscript.size() > 0) {
|
||||
auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
|
||||
if (!getXYSuccess) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Error extracting X and Y from ePub"));
|
||||
}
|
||||
if (sessionTranscript.size() > 0 &&
|
||||
!(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
|
||||
ePubX.size()) != nullptr &&
|
||||
memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
|
||||
ePubY.size()) != nullptr)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Did not find ephemeral public key's X and Y coordinates in "
|
||||
"SessionTranscript (make sure leading zeroes are not used)"));
|
||||
if (session_) {
|
||||
// If presenting in a session, the TA has already done this check.
|
||||
|
||||
} else {
|
||||
// To prevent replay-attacks, we check that the public part of the ephemeral
|
||||
// key we previously created, is present in the DeviceEngagement part of
|
||||
// SessionTranscript as a COSE_Key, in uncompressed form.
|
||||
//
|
||||
// We do this by just searching for the X and Y coordinates.
|
||||
//
|
||||
// Would be nice to move this check to the TA.
|
||||
if (sessionTranscript.size() > 0) {
|
||||
auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
|
||||
if (!getXYSuccess) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Error extracting X and Y from ePub"));
|
||||
}
|
||||
if (sessionTranscript.size() > 0 &&
|
||||
!(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
|
||||
ePubX.size()) != nullptr &&
|
||||
memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
|
||||
ePubY.size()) != nullptr)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Did not find ephemeral public key's X and Y coordinates in "
|
||||
"SessionTranscript (make sure leading zeroes are not used)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// itemsRequest: If non-empty, contains request data that may be signed by the
|
||||
// reader. The content can be defined in the way appropriate for the
|
||||
@@ -537,21 +607,38 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
|
||||
|
||||
// Finally, pass info so the HMAC key can be derived and the TA can start
|
||||
// creating the DeviceNameSpaces CBOR...
|
||||
if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) {
|
||||
// We expect the reader ephemeral public key to be same size and curve
|
||||
// as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
|
||||
// won't work. So its length should be 65 bytes and it should be
|
||||
// starting with 0x04.
|
||||
if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Reader public key is not in expected format"));
|
||||
if (!session_) {
|
||||
if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 &&
|
||||
signingKeyBlob.size() > 0) {
|
||||
// We expect the reader ephemeral public key to be same size and curve
|
||||
// as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
|
||||
// won't work. So its length should be 65 bytes and it should be
|
||||
// starting with 0x04.
|
||||
if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Reader public key is not in expected format"));
|
||||
}
|
||||
vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
|
||||
if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
|
||||
numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error starting retrieving entries"));
|
||||
}
|
||||
}
|
||||
vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
|
||||
if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
|
||||
numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
|
||||
} else {
|
||||
if (session_->getSessionTranscript().size() > 0 &&
|
||||
session_->getReaderEphemeralPublicKey().size() > 0 && signingKeyBlob.size() > 0) {
|
||||
// Don't actually pass the reader ephemeral public key in, the TA will get
|
||||
// it from the session object.
|
||||
//
|
||||
if (!hwProxy_->calcMacKey(sessionTranscript_, {}, signingKeyBlob, docType_,
|
||||
numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error starting retrieving entries"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,6 +752,11 @@ void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileM
|
||||
ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
|
||||
const string& nameSpace, const string& name, int32_t entrySize,
|
||||
const vector<int32_t>& accessControlProfileIds) {
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
|
||||
@@ -785,6 +877,11 @@ ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent,
|
||||
vector<uint8_t>* outContent) {
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
|
||||
encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_);
|
||||
if (!content) {
|
||||
@@ -829,6 +926,11 @@ ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>&
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
|
||||
vector<uint8_t>* outDeviceNameSpaces) {
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
|
||||
deviceNameSpacesMap_.add(currentNameSpace_,
|
||||
std::move(currentNameSpaceDeviceNameSpacesMap_));
|
||||
@@ -846,18 +948,23 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
|
||||
.c_str()));
|
||||
}
|
||||
|
||||
// If there's no signing key or no sessionTranscript or no reader ephemeral
|
||||
// public key, we return the empty MAC.
|
||||
// If the TA calculated a MAC (it might not have), format it as a COSE_Mac0
|
||||
//
|
||||
optional<vector<uint8_t>> mac;
|
||||
if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
|
||||
readerPublicKey_.size() > 0) {
|
||||
optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
|
||||
if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
|
||||
optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
|
||||
|
||||
// The MAC not being set means an error occurred.
|
||||
if (!digestToBeMaced) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Error generating digestToBeMaced"));
|
||||
}
|
||||
// Size 0 means that the MAC isn't set. If it's set, it has to be 32 bytes.
|
||||
if (digestToBeMaced.value().size() != 0) {
|
||||
if (digestToBeMaced.value().size() != 32) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Error generating digestToBeMaced"));
|
||||
"Unexpected size for digestToBeMaced"));
|
||||
}
|
||||
// Now construct COSE_Mac0 from the returned MAC...
|
||||
mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
|
||||
}
|
||||
|
||||
@@ -868,6 +975,15 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
|
||||
vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
ndk::ScopedAStatus status = ensureHwProxy();
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
|
||||
hwProxy_->generateSigningKeyPair(docType_, now);
|
||||
@@ -885,9 +1001,18 @@ ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::updateCredential(
|
||||
shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
|
||||
sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
|
||||
if (session_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session"));
|
||||
}
|
||||
sp<SecureHardwareProvisioningProxy> provisioningHwProxy =
|
||||
hwProxyFactory_->createProvisioningProxy();
|
||||
if (!provisioningHwProxy) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating provisioning proxy"));
|
||||
}
|
||||
shared_ptr<WritableIdentityCredential> wc =
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(provisioningHwProxy, docType_,
|
||||
testCredential_);
|
||||
if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <cppbor.h>
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "PresentationSession.h"
|
||||
#include "SecureHardwareProxy.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
@@ -46,11 +47,11 @@ using ::std::vector;
|
||||
class IdentityCredential : public BnIdentityCredential {
|
||||
public:
|
||||
IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
sp<SecureHardwarePresentationProxy> hwProxy,
|
||||
const vector<uint8_t>& credentialData)
|
||||
const vector<uint8_t>& credentialData,
|
||||
std::shared_ptr<PresentationSession> session)
|
||||
: hwProxyFactory_(hwProxyFactory),
|
||||
hwProxy_(hwProxy),
|
||||
credentialData_(credentialData),
|
||||
session_(std::move(session)),
|
||||
numStartRetrievalCalls_(0),
|
||||
expectedDeviceNameSpacesSize_(0) {}
|
||||
|
||||
@@ -94,10 +95,13 @@ class IdentityCredential : public BnIdentityCredential {
|
||||
bool includeChallenge,
|
||||
vector<uint8_t>* outProofOfDeletionSignature);
|
||||
|
||||
// Creates and initializes hwProxy_.
|
||||
ndk::ScopedAStatus ensureHwProxy();
|
||||
|
||||
// Set by constructor
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||
sp<SecureHardwarePresentationProxy> hwProxy_;
|
||||
vector<uint8_t> credentialData_;
|
||||
shared_ptr<PresentationSession> session_;
|
||||
int numStartRetrievalCalls_;
|
||||
|
||||
// Set by initialize()
|
||||
@@ -105,6 +109,9 @@ class IdentityCredential : public BnIdentityCredential {
|
||||
bool testCredential_;
|
||||
vector<uint8_t> encryptedCredentialKeys_;
|
||||
|
||||
// Set by ensureHwProxy()
|
||||
sp<SecureHardwarePresentationProxy> hwProxy_;
|
||||
|
||||
// Set by createEphemeralKeyPair()
|
||||
vector<uint8_t> ephemeralPublicKey_;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "IdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "PresentationSession.h"
|
||||
#include "WritableIdentityCredential.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
@@ -61,9 +62,8 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential(
|
||||
"Unsupported cipher suite"));
|
||||
}
|
||||
|
||||
sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
|
||||
shared_ptr<IdentityCredential> credential =
|
||||
ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
|
||||
shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(
|
||||
hwProxyFactory_, credentialData, nullptr /* session */);
|
||||
auto ret = credential->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
@@ -73,4 +73,25 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::createPresentationSession(
|
||||
CipherSuite cipherSuite, shared_ptr<IPresentationSession>* outSession) {
|
||||
// We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
|
||||
if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
|
||||
"Unsupported cipher suite"));
|
||||
}
|
||||
|
||||
sp<SecureHardwareSessionProxy> hwProxy = hwProxyFactory_->createSessionProxy();
|
||||
shared_ptr<PresentationSession> session =
|
||||
ndk::SharedRefBase::make<PresentationSession>(hwProxyFactory_, hwProxy);
|
||||
auto ret = session->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
int(ret), "Error initializing PresentationSession"));
|
||||
}
|
||||
*outSession = session;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
@@ -47,6 +47,9 @@ class IdentityCredentialStore : public BnIdentityCredentialStore {
|
||||
ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
|
||||
shared_ptr<IIdentityCredential>* outCredential) override;
|
||||
|
||||
ndk::ScopedAStatus createPresentationSession(
|
||||
CipherSuite cipherSuite, shared_ptr<IPresentationSession>* outSession) override;
|
||||
|
||||
private:
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||
};
|
||||
|
||||
149
identity/aidl/default/common/PresentationSession.cpp
Normal file
149
identity/aidl/default/common/PresentationSession.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "PresentationSession"
|
||||
|
||||
#include "PresentationSession.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
#include "FakeSecureHardwareProxy.h"
|
||||
#include "IdentityCredential.h"
|
||||
#include "PresentationSession.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::std::optional;
|
||||
|
||||
using namespace ::android::hardware::identity;
|
||||
|
||||
PresentationSession::~PresentationSession() {}
|
||||
|
||||
int PresentationSession::initialize() {
|
||||
if (!hwProxy_->initialize()) {
|
||||
LOG(ERROR) << "hwProxy->initialize failed";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
|
||||
optional<uint64_t> id = hwProxy_->getId();
|
||||
if (!id) {
|
||||
LOG(ERROR) << "Error getting id for session";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
id_ = id.value();
|
||||
|
||||
optional<vector<uint8_t>> ephemeralKeyPriv = hwProxy_->getEphemeralKeyPair();
|
||||
if (!ephemeralKeyPriv) {
|
||||
LOG(ERROR) << "Error getting ephemeral private key for session";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
optional<vector<uint8_t>> ephemeralKeyPair =
|
||||
support::ecPrivateKeyToKeyPair(ephemeralKeyPriv.value());
|
||||
if (!ephemeralKeyPair) {
|
||||
LOG(ERROR) << "Error creating ephemeral key-pair";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
ephemeralKeyPair_ = ephemeralKeyPair.value();
|
||||
|
||||
optional<uint64_t> authChallenge = hwProxy_->getAuthChallenge();
|
||||
if (!authChallenge) {
|
||||
LOG(ERROR) << "Error getting authChallenge for session";
|
||||
return IIdentityCredentialStore::STATUS_FAILED;
|
||||
}
|
||||
authChallenge_ = authChallenge.value();
|
||||
|
||||
return IIdentityCredentialStore::STATUS_OK;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus PresentationSession::getEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
|
||||
*outKeyPair = ephemeralKeyPair_;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus PresentationSession::getAuthChallenge(int64_t* outChallenge) {
|
||||
*outChallenge = authChallenge_;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus PresentationSession::setReaderEphemeralPublicKey(
|
||||
const vector<uint8_t>& publicKey) {
|
||||
// We expect the reader ephemeral public key to be same size and curve
|
||||
// as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
|
||||
// won't work. So its length should be 65 bytes and it should be
|
||||
// starting with 0x04.
|
||||
if (publicKey.size() != 65 || publicKey[0] != 0x04) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Reader public key is not in expected format"));
|
||||
}
|
||||
readerPublicKey_ = publicKey;
|
||||
vector<uint8_t> pubKeyP256(publicKey.begin() + 1, publicKey.end());
|
||||
if (!hwProxy_->setReaderEphemeralPublicKey(pubKeyP256)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error setting readerEphemeralPublicKey for session"));
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus PresentationSession::setSessionTranscript(
|
||||
const vector<uint8_t>& sessionTranscript) {
|
||||
sessionTranscript_ = sessionTranscript;
|
||||
if (!hwProxy_->setSessionTranscript(sessionTranscript)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error setting SessionTranscript for session"));
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus PresentationSession::getCredential(
|
||||
const vector<uint8_t>& credentialData, shared_ptr<IIdentityCredential>* outCredential) {
|
||||
shared_ptr<PresentationSession> p = ref<PresentationSession>();
|
||||
shared_ptr<IdentityCredential> credential =
|
||||
ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, credentialData, p);
|
||||
int ret = credential->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
ret, "Error initializing IdentityCredential"));
|
||||
}
|
||||
*outCredential = std::move(credential);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
uint64_t PresentationSession::getSessionId() {
|
||||
return id_;
|
||||
}
|
||||
|
||||
vector<uint8_t> PresentationSession::getSessionTranscript() {
|
||||
return sessionTranscript_;
|
||||
}
|
||||
|
||||
vector<uint8_t> PresentationSession::getReaderEphemeralPublicKey() {
|
||||
return readerPublicKey_;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
83
identity/aidl/default/common/PresentationSession.h
Normal file
83
identity/aidl/default/common/PresentationSession.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnPresentationSession.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cppbor.h>
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "SecureHardwareProxy.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::aidl::android::hardware::keymaster::HardwareAuthToken;
|
||||
using ::aidl::android::hardware::keymaster::VerificationToken;
|
||||
using ::android::sp;
|
||||
using ::android::hardware::identity::SecureHardwareSessionProxy;
|
||||
using ::std::vector;
|
||||
|
||||
class PresentationSession : public BnPresentationSession {
|
||||
public:
|
||||
PresentationSession(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
sp<SecureHardwareSessionProxy> hwProxy)
|
||||
: hwProxyFactory_(std::move(hwProxyFactory)), hwProxy_(std::move(hwProxy)) {}
|
||||
|
||||
virtual ~PresentationSession();
|
||||
|
||||
// Creates ephemeral key and auth-challenge in TA. Returns a status code from
|
||||
// IIdentityCredentialStore. Must be called right after construction.
|
||||
int initialize();
|
||||
|
||||
uint64_t getSessionId();
|
||||
|
||||
vector<uint8_t> getSessionTranscript();
|
||||
vector<uint8_t> getReaderEphemeralPublicKey();
|
||||
|
||||
// Methods from IPresentationSession follow.
|
||||
ndk::ScopedAStatus getEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
|
||||
ndk::ScopedAStatus getAuthChallenge(int64_t* outChallenge) override;
|
||||
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
|
||||
ndk::ScopedAStatus setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
|
||||
|
||||
ndk::ScopedAStatus getCredential(const vector<uint8_t>& credentialData,
|
||||
shared_ptr<IIdentityCredential>* outCredential) override;
|
||||
|
||||
private:
|
||||
// Set by constructor
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||
sp<SecureHardwareSessionProxy> hwProxy_;
|
||||
|
||||
// Set by initialize()
|
||||
uint64_t id_;
|
||||
vector<uint8_t> ephemeralKeyPair_;
|
||||
uint64_t authChallenge_;
|
||||
|
||||
// Set by setReaderEphemeralPublicKey()
|
||||
vector<uint8_t> readerPublicKey_;
|
||||
|
||||
// Set by setSessionTranscript()
|
||||
vector<uint8_t> sessionTranscript_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H
|
||||
@@ -42,6 +42,7 @@ using ::std::vector;
|
||||
// Forward declare.
|
||||
//
|
||||
class SecureHardwareProvisioningProxy;
|
||||
class SecureHardwareSessionProxy;
|
||||
class SecureHardwarePresentationProxy;
|
||||
|
||||
// This is a class used to create proxies.
|
||||
@@ -52,6 +53,7 @@ class SecureHardwareProxyFactory : public RefBase {
|
||||
virtual ~SecureHardwareProxyFactory() {}
|
||||
|
||||
virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0;
|
||||
virtual sp<SecureHardwareSessionProxy> createSessionProxy() = 0;
|
||||
virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0;
|
||||
};
|
||||
|
||||
@@ -64,8 +66,12 @@ class SecureHardwareProvisioningProxy : public RefBase {
|
||||
|
||||
virtual bool initialize(bool testCredential) = 0;
|
||||
|
||||
virtual bool initializeForUpdate(bool testCredential, string docType,
|
||||
vector<uint8_t> encryptedCredentialKeys) = 0;
|
||||
virtual bool initializeForUpdate(bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) = 0;
|
||||
|
||||
virtual optional<uint32_t> getId() = 0;
|
||||
|
||||
virtual bool shutdown() = 0;
|
||||
|
||||
// Returns public key certificate chain with attestation.
|
||||
//
|
||||
@@ -76,7 +82,7 @@ class SecureHardwareProvisioningProxy : public RefBase {
|
||||
virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& applicationId) = 0;
|
||||
|
||||
virtual bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
|
||||
virtual bool startPersonalization(int accessControlProfileCount, const vector<int>& entryCounts,
|
||||
const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) = 0;
|
||||
|
||||
@@ -98,8 +104,6 @@ class SecureHardwareProvisioningProxy : public RefBase {
|
||||
|
||||
// Returns encryptedCredentialKeys (80 bytes).
|
||||
virtual optional<vector<uint8_t>> finishGetCredentialData(const string& docType) = 0;
|
||||
|
||||
virtual bool shutdown() = 0;
|
||||
};
|
||||
|
||||
enum AccessCheckResult {
|
||||
@@ -110,6 +114,30 @@ enum AccessCheckResult {
|
||||
kReaderAuthenticationFailed,
|
||||
};
|
||||
|
||||
// The proxy used for sessions.
|
||||
//
|
||||
class SecureHardwareSessionProxy : public RefBase {
|
||||
public:
|
||||
SecureHardwareSessionProxy() {}
|
||||
|
||||
virtual ~SecureHardwareSessionProxy() {}
|
||||
|
||||
virtual bool initialize() = 0;
|
||||
|
||||
virtual optional<uint32_t> getId() = 0;
|
||||
|
||||
virtual bool shutdown() = 0;
|
||||
|
||||
virtual optional<uint64_t> getAuthChallenge() = 0;
|
||||
|
||||
// Returns private key
|
||||
virtual optional<vector<uint8_t>> getEphemeralKeyPair() = 0;
|
||||
|
||||
virtual bool setReaderEphemeralPublicKey(const vector<uint8_t>& readerEphemeralPublicKey) = 0;
|
||||
|
||||
virtual bool setSessionTranscript(const vector<uint8_t>& sessionTranscript) = 0;
|
||||
};
|
||||
|
||||
// The proxy used for presentation.
|
||||
//
|
||||
class SecureHardwarePresentationProxy : public RefBase {
|
||||
@@ -117,12 +145,16 @@ class SecureHardwarePresentationProxy : public RefBase {
|
||||
SecureHardwarePresentationProxy() {}
|
||||
virtual ~SecureHardwarePresentationProxy() {}
|
||||
|
||||
virtual bool initialize(bool testCredential, string docType,
|
||||
vector<uint8_t> encryptedCredentialKeys) = 0;
|
||||
virtual bool initialize(uint32_t sessionId, bool testCredential, const string& docType,
|
||||
const vector<uint8_t>& encryptedCredentialKeys) = 0;
|
||||
|
||||
virtual optional<uint32_t> getId() = 0;
|
||||
|
||||
virtual bool shutdown() = 0;
|
||||
|
||||
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
|
||||
virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
|
||||
time_t now) = 0;
|
||||
virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(
|
||||
const string& docType, time_t now) = 0;
|
||||
|
||||
// Returns private key
|
||||
virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0;
|
||||
@@ -174,8 +206,6 @@ class SecureHardwarePresentationProxy : public RefBase {
|
||||
virtual optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
|
||||
const vector<uint8_t>& challenge,
|
||||
size_t proofOfOwnershipCborSize) = 0;
|
||||
|
||||
virtual bool shutdown() = 0;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::identity
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.identity</name>
|
||||
<version>3</version>
|
||||
<version>4</version>
|
||||
<interface>
|
||||
<name>IIdentityCredentialStore</name>
|
||||
<instance>default</instance>
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#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 = [
|
||||
|
||||
@@ -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).
|
||||
@@ -295,6 +305,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
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
|
||||
#include "EicPresentation.h"
|
||||
#include "EicCommon.h"
|
||||
#include "EicSession.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
|
||||
size_t docTypeLength, const uint8_t* encryptedCredentialKeys,
|
||||
// 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[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
|
||||
bool expectPopSha256 = false;
|
||||
@@ -39,6 +45,13 @@ 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,
|
||||
@@ -86,6 +99,23 @@ 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;
|
||||
}
|
||||
|
||||
@@ -174,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;
|
||||
@@ -190,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;
|
||||
@@ -330,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,
|
||||
@@ -338,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;
|
||||
@@ -354,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;
|
||||
@@ -377,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;
|
||||
}
|
||||
}
|
||||
@@ -490,6 +563,25 @@ bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTrans
|
||||
const uint8_t signingKeyBlob[60], const char* docType,
|
||||
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,
|
||||
docTypeLength, signingKeyPriv)) {
|
||||
|
||||
@@ -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,10 +103,18 @@ typedef struct {
|
||||
EicCbor cbor;
|
||||
} EicPresentation;
|
||||
|
||||
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
|
||||
size_t docTypeLength, const uint8_t* encryptedCredentialKeys,
|
||||
// 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 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,
|
||||
|
||||
@@ -17,8 +17,21 @@
|
||||
#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;
|
||||
@@ -47,6 +60,13 @@ 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,
|
||||
@@ -96,6 +116,21 @@ 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,
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -68,6 +71,10 @@ bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, con
|
||||
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,
|
||||
|
||||
120
identity/aidl/default/libeic/EicSession.c
Normal file
120
identity/aidl/default/libeic/EicSession.c
Normal file
@@ -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;
|
||||
}
|
||||
73
identity/aidl/default/libeic/EicSession.h
Normal file
73
identity/aidl/default/libeic/EicSession.h
Normal file
@@ -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
|
||||
@@ -27,10 +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 "EicCommon.h"
|
||||
#include "EicSession.h"
|
||||
#undef EIC_INSIDE_LIBEIC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -28,6 +28,7 @@ cc_test {
|
||||
"EndToEndTests.cpp",
|
||||
"TestCredentialTests.cpp",
|
||||
"AuthenticationKeyTests.cpp",
|
||||
"PresentationSessionTests.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
@@ -40,9 +41,9 @@ cc_test {
|
||||
"libpuresoftkeymasterdevice",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity-V3-cpp",
|
||||
"android.hardware.keymaster-V3-cpp",
|
||||
"android.hardware.keymaster-V3-ndk",
|
||||
"android.hardware.identity-V4-cpp",
|
||||
"android.hardware.keymaster-V4-cpp",
|
||||
"android.hardware.keymaster-V4-ndk",
|
||||
"libkeymaster4support",
|
||||
"libkeymaster4_1support",
|
||||
],
|
||||
|
||||
197
identity/aidl/vts/PresentationSessionTests.cpp
Normal file
197
identity/aidl/vts/PresentationSessionTests.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "PresentationSessionTests"
|
||||
|
||||
#include <aidl/Gtest.h>
|
||||
#include <aidl/Vintf.h>
|
||||
#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
|
||||
#include <aidl/android/hardware/keymaster/VerificationToken.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/identity/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
namespace android::hardware::identity {
|
||||
|
||||
using std::endl;
|
||||
using std::make_pair;
|
||||
using std::map;
|
||||
using std::optional;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using std::tie;
|
||||
using std::vector;
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::String16;
|
||||
using ::android::binder::Status;
|
||||
|
||||
using ::android::hardware::keymaster::HardwareAuthToken;
|
||||
using ::android::hardware::keymaster::VerificationToken;
|
||||
|
||||
class PresentationSessionTests : public testing::TestWithParam<string> {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
|
||||
String16(GetParam().c_str()));
|
||||
ASSERT_NE(credentialStore_, nullptr);
|
||||
halApiVersion_ = credentialStore_->getInterfaceVersion();
|
||||
}
|
||||
|
||||
void provisionData();
|
||||
|
||||
void provisionSingleDocument(const string& docType, vector<uint8_t>* outCredentialData,
|
||||
vector<uint8_t>* outCredentialPubKey);
|
||||
|
||||
// Set by provisionData
|
||||
vector<uint8_t> credential1Data_;
|
||||
vector<uint8_t> credential1PubKey_;
|
||||
vector<uint8_t> credential2Data_;
|
||||
vector<uint8_t> credential2PubKey_;
|
||||
|
||||
sp<IIdentityCredentialStore> credentialStore_;
|
||||
int halApiVersion_;
|
||||
};
|
||||
|
||||
void PresentationSessionTests::provisionData() {
|
||||
provisionSingleDocument("org.iso.18013-5.2019.mdl", &credential1Data_, &credential1PubKey_);
|
||||
provisionSingleDocument("org.blah.OtherhDocTypeXX", &credential2Data_, &credential2PubKey_);
|
||||
}
|
||||
|
||||
void PresentationSessionTests::provisionSingleDocument(const string& docType,
|
||||
vector<uint8_t>* outCredentialData,
|
||||
vector<uint8_t>* outCredentialPubKey) {
|
||||
bool testCredential = true;
|
||||
sp<IWritableIdentityCredential> wc;
|
||||
ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
|
||||
|
||||
vector<uint8_t> attestationApplicationId;
|
||||
vector<uint8_t> attestationChallenge = {1};
|
||||
vector<Certificate> certChain;
|
||||
ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
|
||||
&certChain)
|
||||
.isOk());
|
||||
|
||||
optional<vector<uint8_t>> optCredentialPubKey =
|
||||
support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
|
||||
ASSERT_TRUE(optCredentialPubKey);
|
||||
*outCredentialPubKey = optCredentialPubKey.value();
|
||||
|
||||
size_t proofOfProvisioningSize = 106;
|
||||
// Not in v1 HAL, may fail
|
||||
wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
|
||||
|
||||
ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
|
||||
{1} /* numDataElementsPerNamespace */)
|
||||
.isOk());
|
||||
|
||||
// Access control profile 0: open access - don't care about the returned SACP
|
||||
SecureAccessControlProfile sacp;
|
||||
ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
|
||||
|
||||
// Single entry - don't care about the returned encrypted data
|
||||
ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Some Data", 1).isOk());
|
||||
vector<uint8_t> encryptedData;
|
||||
ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
|
||||
|
||||
vector<uint8_t> proofOfProvisioningSignature;
|
||||
Status status = wc->finishAddingEntries(outCredentialData, &proofOfProvisioningSignature);
|
||||
EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
|
||||
}
|
||||
|
||||
// This checks that any methods called on an IIdentityCredential obtained via a session
|
||||
// returns STATUS_FAILED except for startRetrieval(), startRetrieveEntryValue(),
|
||||
// retrieveEntryValue(), finishRetrieval(), setRequestedNamespaces(), setVerificationToken()
|
||||
//
|
||||
TEST_P(PresentationSessionTests, returnsFailureOnUnsupportedMethods) {
|
||||
if (halApiVersion_ < 4) {
|
||||
GTEST_SKIP() << "Need HAL API version 4, have " << halApiVersion_;
|
||||
}
|
||||
|
||||
provisionData();
|
||||
|
||||
sp<IPresentationSession> session;
|
||||
ASSERT_TRUE(credentialStore_
|
||||
->createPresentationSession(
|
||||
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
|
||||
&session)
|
||||
.isOk());
|
||||
|
||||
sp<IIdentityCredential> credential;
|
||||
ASSERT_TRUE(session->getCredential(credential1Data_, &credential).isOk());
|
||||
|
||||
Status result;
|
||||
|
||||
vector<uint8_t> signatureProofOfDeletion;
|
||||
result = credential->deleteCredential(&signatureProofOfDeletion);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
vector<uint8_t> ephemeralKeyPair;
|
||||
result = credential->createEphemeralKeyPair(&ephemeralKeyPair);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
result = credential->setReaderEphemeralPublicKey({});
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
int64_t authChallenge;
|
||||
result = credential->createAuthChallenge(&authChallenge);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
Certificate certificate;
|
||||
vector<uint8_t> signingKeyBlob;
|
||||
result = credential->generateSigningKeyPair(&signingKeyBlob, &certificate);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
result = credential->deleteCredentialWithChallenge({}, &signatureProofOfDeletion);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
vector<uint8_t> signatureProofOfOwnership;
|
||||
result = credential->proveOwnership({}, &signatureProofOfOwnership);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
|
||||
sp<IWritableIdentityCredential> writableCredential;
|
||||
result = credential->updateCredential(&writableCredential);
|
||||
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
|
||||
EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
|
||||
}
|
||||
|
||||
// TODO: need to add tests to check that the returned IIdentityCredential works
|
||||
// as intended.
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PresentationSessionTests);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Identity, PresentationSessionTests,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
||||
|
||||
} // namespace android::hardware::identity
|
||||
Reference in New Issue
Block a user