Identity Credential changes for Android 12
- Add IIdentityCredential.deleteCredentialWithChallenge()
- Deprecate IIdentityCredential.deleteCredential()
- Add IIdentityCredential.proveOwership()
- Add IIdentityCredential.updateCredential()
- Add ProofOfBinding CBOR to AuthenticationKey X.509 certificate
- Document which API versions new methods/features appeared in.
- Mention need to declare android.hardware.identity_credential system
feature (w/ feature version number) and do this for the default
implementation.
Bug: 170146643
Test: atest VtsHalIdentityTargetTest
Change-Id: Ib47c7caa5f3d6fff6919f019eee44a735dba9cf8
diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
index 270fcfa..9477997 100644
--- a/identity/aidl/default/common/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -30,6 +30,7 @@
#include <cppbor_parse.h>
#include "FakeSecureHardwareProxy.h"
+#include "WritableIdentityCredential.h"
namespace aidl::android::hardware::identity {
@@ -70,14 +71,8 @@
docType_ = docTypeItem->value();
testCredential_ = testCredentialItem->value();
- const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-
- if (encryptedCredentialKeys.size() != 80) {
- LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
- return IIdentityCredentialStore::STATUS_INVALID_DATA;
- }
-
- if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
+ encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
+ if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
LOG(ERROR) << "hwProxy->initialize failed";
return false;
}
@@ -87,12 +82,32 @@
ndk::ScopedAStatus IdentityCredential::deleteCredential(
vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
+ const vector<uint8_t>& challenge, bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ if (includeChallenge) {
+ array = {"ProofOfDeletion", docType_, challenge, testCredential_};
+ }
+
vector<uint8_t> proofOfDeletionCbor = array.encode();
vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
- optional<vector<uint8_t>> signatureOfToBeSigned =
- hwProxy_->deleteCredential(docType_, proofOfDeletionCbor.size());
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
+ docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
if (!signatureOfToBeSigned) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
@@ -111,6 +126,38 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus IdentityCredential::proveOwnership(
+ const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
+ cppbor::Array array;
+ array = {"ProofOfOwnership", docType_, challenge, testCredential_};
+ vector<uint8_t> proofOfOwnershipCbor = array.encode();
+ vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
+
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
+ docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfOwnership"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ proofOfOwnershipCbor, // data
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfOwnershipSignature = signature.value();
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
if (!ephemeralPriv) {
@@ -833,4 +880,19 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus IdentityCredential::updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
+ testCredential_);
+ if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential for update"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
index 2281821..9913b86 100644
--- a/identity/aidl/default/common/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -45,9 +45,11 @@
class IdentityCredential : public BnIdentityCredential {
public:
- IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
+ IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
+ sp<SecureHardwarePresentationProxy> hwProxy,
const vector<uint8_t>& credentialData)
- : hwProxy_(hwProxy),
+ : hwProxyFactory_(hwProxyFactory),
+ hwProxy_(hwProxy),
credentialData_(credentialData),
numStartRetrievalCalls_(0),
expectedDeviceNameSpacesSize_(0) {}
@@ -58,6 +60,11 @@
// Methods from IIdentityCredential follow.
ndk::ScopedAStatus deleteCredential(vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus proveOwnership(const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfOwnershipSignature) override;
ndk::ScopedAStatus createEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
@@ -79,8 +86,16 @@
ndk::ScopedAStatus generateSigningKeyPair(vector<uint8_t>* outSigningKeyBlob,
Certificate* outSigningKeyCertificate) override;
+ ndk::ScopedAStatus updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
private:
+ ndk::ScopedAStatus deleteCredentialCommon(const vector<uint8_t>& challenge,
+ bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature);
+
// Set by constructor
+ sp<SecureHardwareProxyFactory> hwProxyFactory_;
sp<SecureHardwarePresentationProxy> hwProxy_;
vector<uint8_t> credentialData_;
int numStartRetrievalCalls_;
@@ -88,6 +103,7 @@
// Set by initialize()
string docType_;
bool testCredential_;
+ vector<uint8_t> encryptedCredentialKeys_;
// Set by createEphemeralKeyPair()
vector<uint8_t> ephemeralPublicKey_;
diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
index 13f91aa..e6b5466 100644
--- a/identity/aidl/default/common/IdentityCredentialStore.cpp
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -63,7 +63,7 @@
sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
shared_ptr<IdentityCredential> credential =
- ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
+ ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
auto ret = credential->initialize();
if (ret != IIdentityCredentialStore::STATUS_OK) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
index b89ad87..a1ed1ef 100644
--- a/identity/aidl/default/common/SecureHardwareProxy.h
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -64,6 +64,9 @@
virtual bool initialize(bool testCredential) = 0;
+ virtual bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) = 0;
+
// Returns public key certificate chain with attestation.
//
// This must return an entire certificate chain and its implementation must
@@ -164,8 +167,14 @@
virtual optional<vector<uint8_t>> finishRetrieval();
virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
+ const vector<uint8_t>& challenge,
+ bool includeChallenge,
size_t proofOfDeletionCborSize) = 0;
+ virtual optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) = 0;
+
virtual bool shutdown() = 0;
};
diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
index 1328f36..2d897c7 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -40,7 +40,20 @@
bool WritableIdentityCredential::initialize() {
if (!hwProxy_->initialize(testCredential_)) {
- LOG(ERROR) << "hwProxy->initialize failed";
+ LOG(ERROR) << "hwProxy->initialize() failed";
+ return false;
+ }
+ startPersonalizationCalled_ = false;
+ firstEntry_ = true;
+
+ return true;
+}
+
+// Used when updating a credential. Returns false on failure.
+bool WritableIdentityCredential::initializeForUpdate(
+ const vector<uint8_t>& encryptedCredentialKeys) {
+ if (!hwProxy_->initializeForUpdate(testCredential_, docType_, encryptedCredentialKeys)) {
+ LOG(ERROR) << "hwProxy->initializeForUpdate() failed";
return false;
}
startPersonalizationCalled_ = false;
diff --git a/identity/aidl/default/common/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
index c6f0628..36ad430 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.h
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -36,16 +36,22 @@
class WritableIdentityCredential : public BnWritableIdentityCredential {
public:
+ // For a new credential, call initialize() right after construction.
+ //
+ // For an updated credential, call initializeForUpdate() right after construction.
+ //
WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
bool testCredential)
: hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
~WritableIdentityCredential();
- // Creates the Credential Key. Returns false on failure. Must be called
- // right after construction.
+ // Creates the Credential Key. Returns false on failure.
bool initialize();
+ // Used when updating a credential. Returns false on failure.
+ bool initializeForUpdate(const vector<uint8_t>& encryptedCredentialKeys);
+
// Methods from IWritableIdentityCredential follow.
ndk::ScopedAStatus getAttestationCertificate(const vector<uint8_t>& attestationApplicationId,
const vector<uint8_t>& attestationChallenge,