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/Android.bp b/identity/aidl/default/Android.bp
index 7f342d0..8744648 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -12,6 +12,7 @@
cflags: [
"-Wall",
"-Wextra",
+ "-Wno-deprecated-declarations",
],
shared_libs: [
"liblog",
@@ -28,8 +29,8 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-ndk_platform",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-ndk_platform",
+ "android.hardware.keymaster-unstable-ndk_platform",
],
}
@@ -88,8 +89,8 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-ndk_platform",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-ndk_platform",
+ "android.hardware.keymaster-unstable-ndk_platform",
"android.hardware.identity-libeic-hal-common",
"android.hardware.identity-libeic-library",
],
@@ -97,4 +98,14 @@
"service.cpp",
"FakeSecureHardwareProxy.cpp",
],
+ required: [
+ "android.hardware.identity_credential.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.identity_credential.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.identity_credential.xml",
}
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
index 3f2ec8b..8ec4cc9 100644
--- a/identity/aidl/default/EicOpsImpl.cc
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -45,6 +45,7 @@
#include "EicOps.h"
+using ::std::map;
using ::std::optional;
using ::std::string;
using ::std::tuple;
@@ -212,7 +213,8 @@
return false;
}
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
- eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
return false;
}
@@ -224,7 +226,7 @@
}
// ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
- eicDebug("Private key is %zd bytes long, expected %zd", pubKey.value().size(),
+ eicDebug("Public key is %zd bytes long, expected %zd", pubKey.value().size(),
(size_t)EIC_P256_PRIV_KEY_SIZE + 1);
return false;
}
@@ -272,7 +274,8 @@
return false;
}
if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
- eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
return false;
}
@@ -284,8 +287,8 @@
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
const char* issuerName, const char* subjectName, time_t validityNotBefore,
- time_t validityNotAfter, uint8_t* cert,
- size_t* certSize) { // inout
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert, size_t* certSize) { // inout
vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
@@ -293,12 +296,18 @@
pubKeyVec[0] = 0x04;
memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
- std::string serialDecimal = android::base::StringPrintf("%d", serial);
+ string serialDecimal = android::base::StringPrintf("%d", serial);
+
+ map<string, vector<uint8_t>> extensions;
+ if (proofOfBinding != nullptr) {
+ vector<uint8_t> proofOfBindingVec(proofOfBinding, proofOfBinding + proofOfBindingSize);
+ extensions["1.3.6.1.4.1.11129.2.1.26"] = proofOfBindingVec;
+ }
optional<vector<uint8_t>> certVec =
android::hardware::identity::support::ecPublicKeyGenerateCertificate(
pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
- validityNotBefore, validityNotAfter);
+ validityNotBefore, validityNotAfter, extensions);
if (!certVec) {
eicDebug("Error generating certificate");
return false;
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
index de6762f..287ffb8 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.cpp
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -67,6 +67,13 @@
return eicProvisioningInit(&ctx_, testCredential);
}
+bool FakeSecureHardwareProvisioningProxy::initializeForUpdate(
+ bool testCredential, string docType, vector<uint8_t> encryptedCredentialKeys) {
+ return eicProvisioningInitForUpdate(&ctx_, testCredential, docType.c_str(),
+ encryptedCredentialKeys.data(),
+ encryptedCredentialKeys.size());
+}
+
// Returns public key certificate.
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
@@ -140,14 +147,16 @@
return signatureOfToBeSigned;
}
-// Returns encryptedCredentialKeys (80 bytes).
+// Returns encryptedCredentialKeys.
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
const string& docType) {
- vector<uint8_t> encryptedCredentialKeys(80);
+ vector<uint8_t> encryptedCredentialKeys(116);
+ size_t size = encryptedCredentialKeys.size();
if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
- encryptedCredentialKeys.data())) {
+ encryptedCredentialKeys.data(), &size)) {
return {};
}
+ encryptedCredentialKeys.resize(size);
return encryptedCredentialKeys;
}
@@ -162,7 +171,7 @@
LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
<< sizeof(EicPresentation);
return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
- encryptedCredentialKeys.data());
+ encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
}
// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
@@ -312,13 +321,27 @@
}
optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
- const string& docType, size_t proofOfDeletionCborSize) {
+ const string& docType, const vector<uint8_t>& challenge, bool includeChallenge,
+ size_t proofOfDeletionCborSize) {
vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
- if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), proofOfDeletionCborSize,
+ if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), challenge.data(), challenge.size(),
+ includeChallenge, proofOfDeletionCborSize,
signatureOfToBeSigned.data())) {
return {};
}
return signatureOfToBeSigned;
}
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::proveOwnership(
+ const string& docType, bool testCredential, const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicPresentationProveOwnership(&ctx_, docType.c_str(), testCredential, challenge.data(),
+ challenge.size(), proofOfOwnershipCborSize,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
} // namespace android::hardware::identity
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h
index b858dd4..6852c1a 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.h
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -32,6 +32,9 @@
bool initialize(bool testCredential) override;
+ bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) override;
+
bool shutdown() override;
// Returns public key certificate.
@@ -122,8 +125,14 @@
optional<vector<uint8_t>> finishRetrieval() override;
optional<vector<uint8_t>> deleteCredential(const string& docType,
+ const vector<uint8_t>& challenge,
+ bool includeChallenge,
size_t proofOfDeletionCborSize) override;
+ optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) override;
+
bool shutdown() override;
protected:
diff --git a/identity/aidl/default/android.hardware.identity_credential.xml b/identity/aidl/default/android.hardware.identity_credential.xml
new file mode 100644
index 0000000..5149792
--- /dev/null
+++ b/identity/aidl/default/android.hardware.identity_credential.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<permissions>
+ <feature name="android.hardware.identity_credential" version="202101" />
+</permissions>
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,
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
index 37d5b81..a074250 100644
--- a/identity/aidl/default/identity-default.xml
+++ b/identity/aidl/default/identity-default.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.identity</name>
- <version>2</version>
+ <version>3</version>
<interface>
<name>IIdentityCredentialStore</name>
<instance>default</instance>
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
index ec049b1..fe131eb 100644
--- a/identity/aidl/default/libeic/EicCbor.c
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -17,6 +17,7 @@
#include "EicCbor.h"
void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
cbor->size = 0;
cbor->bufferSize = bufferSize;
cbor->buffer = buffer;
@@ -26,6 +27,7 @@
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
const uint8_t* hmacKey, size_t hmacKeySize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
cbor->size = 0;
cbor->bufferSize = bufferSize;
cbor->buffer = buffer;
@@ -33,6 +35,10 @@
eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
}
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256) {
+ cbor->secondaryDigesterSha256 = sha256;
+}
+
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
switch (cbor->digestType) {
case EIC_CBOR_DIGEST_TYPE_SHA256:
@@ -53,6 +59,9 @@
eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
break;
}
+ if (cbor->secondaryDigesterSha256 != NULL) {
+ eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
+ }
if (cbor->size >= cbor->bufferSize) {
cbor->size += size;
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
index 4686b38..9c0f531 100644
--- a/identity/aidl/default/libeic/EicCbor.h
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -53,6 +53,9 @@
EicHmacSha256Ctx hmacSha256;
} digester;
+ // The secondary digester, may be unset.
+ EicSha256Ctx* secondaryDigesterSha256;
+
// The buffer used for building up CBOR or NULL if bufferSize is 0.
uint8_t* buffer;
} EicCbor;
@@ -70,6 +73,14 @@
void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
const uint8_t* hmacKey, size_t hmacKeySize);
+/* Enables a secondary digester.
+ *
+ * May be enabled midway through processing, this can be used to e.g. calculate
+ * a digest of Sig_structure (for COSE_Sign1) and a separate digest of its
+ * payload.
+ */
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256);
+
/* Finishes building CBOR and returns the digest. */
void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
index da4dabf..d4fcf0e 100644
--- a/identity/aidl/default/libeic/EicOps.h
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -207,14 +207,17 @@
// Generate an X.509 certificate for the key identified by |publicKey| which
// must be of the form returned by eicOpsCreateEcKey().
//
+// If proofOfBinding is not NULL, it will be included as an OCTET_STRING
+// X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26.
+//
// The certificate will be signed by the key identified by |signingKey| which
// must be of the form returned by eicOpsCreateEcKey().
//
bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
const char* issuerName, const char* subjectName, time_t validityNotBefore,
- time_t validityNotAfter, uint8_t* cert,
- size_t* certSize); // inout
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert, size_t* certSize); // inout
// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
// be given by |digestOfData|). Returns the signature in |signature|.
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
index d3f5556..5e9a280 100644
--- a/identity/aidl/default/libeic/EicPresentation.c
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -19,13 +19,28 @@
#include <inttypes.h>
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
- const uint8_t encryptedCredentialKeys[80]) {
- uint8_t credentialKeys[52];
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[86];
+ bool expectPopSha256 = false;
+
+ // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+ // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+ // to support loading all feature versions.
+ //
+ if (encryptedCredentialKeysSize == 52 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize == 86 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+ return false;
+ }
eicMemSet(ctx, '\0', sizeof(EicPresentation));
if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
- 80,
+ encryptedCredentialKeysSize,
// DocType is the additionalAuthenticatedData
(const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
eicDebug("Error decrypting CredentialKeys");
@@ -34,25 +49,42 @@
// It's supposed to look like this;
//
+ // Feature version 202009:
+ //
// CredentialKeys = [
// bstr, ; storageKey, a 128-bit AES key
- // bstr ; credentialPrivKey, the private key for credentialKey
+ // bstr, ; credentialPrivKey, the private key for credentialKey
// ]
//
- // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
+ // Feature version 202101:
//
- // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
- // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
- // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
//
- if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
- credentialKeys[19] != 0x20) {
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+ // SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) { // 32-byte bstr
eicDebug("Invalid CBOR for CredentialKeys");
return false;
}
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
ctx->testCredential = testCredential;
+ if (expectPopSha256) {
+ eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE);
+ }
return true;
}
@@ -61,6 +93,35 @@
uint8_t signingKeyBlob[60]) {
uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+ uint8_t cborBuf[64];
+
+ // Generate the ProofOfBinding CBOR to include in the X.509 certificate in
+ // IdentityCredentialAuthenticationKeyExtension CBOR. This CBOR is defined
+ // by the following CDDL
+ //
+ // ProofOfBinding = [
+ // "ProofOfBinding",
+ // bstr, // Contains the SHA-256 of ProofOfProvisioning
+ // ]
+ //
+ // This array may grow in the future if other information needs to be
+ // conveyed.
+ //
+ // The bytes of ProofOfBinding is is represented as an OCTET_STRING
+ // and stored at OID 1.3.6.1.4.1.11129.2.1.26.
+ //
+
+ EicCbor cbor;
+ eicCborInit(&cbor, cborBuf, sizeof cborBuf);
+ eicCborAppendArray(&cbor, 2);
+ eicCborAppendString(&cbor, "ProofOfBinding");
+ eicCborAppendByteString(&cbor, ctx->proofOfProvisioningSha256, EIC_SHA256_DIGEST_SIZE);
+ if (cbor.size > sizeof(cborBuf)) {
+ eicDebug("Exceeded buffer size");
+ return false;
+ }
+ const uint8_t* proofOfBinding = cborBuf;
+ size_t proofOfBindingSize = cbor.size;
if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
eicDebug("Error creating signing key");
@@ -73,7 +134,8 @@
if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
"Android Identity Credential Key", // issuer CN
"Android Identity Credential Authentication Key", // subject CN
- validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
+ validityNotBefore, validityNotAfter, proofOfBinding, proofOfBindingSize,
+ publicKeyCert, publicKeyCertSize)) {
eicDebug("Error creating certificate for signing key");
return false;
}
@@ -674,7 +736,8 @@
}
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
- size_t proofOfDeletionCborSize,
+ const uint8_t* challenge, size_t challengeSize,
+ bool includeChallenge, size_t proofOfDeletionCborSize,
uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
EicCbor cbor;
@@ -712,9 +775,12 @@
eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
// Finally, the CBOR that we're actually signing.
- eicCborAppendArray(&cbor, 3);
+ eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
eicCborAppendString(&cbor, "ProofOfDeletion");
eicCborAppendString(&cbor, docType);
+ if (includeChallenge) {
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ }
eicCborAppendBool(&cbor, ctx->testCredential);
uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
@@ -726,3 +792,59 @@
return true;
}
+
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+ const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ EicCbor cbor;
+
+ eicCborInit(&cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfOwnershipCborSize);
+
+ // Finally, the CBOR that we're actually signing.
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "ProofOfOwnership");
+ eicCborAppendString(&cbor, docType);
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ eicCborAppendBool(&cbor, testCredential);
+
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, cborSha256);
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfDeletion");
+ return false;
+ }
+
+ return true;
+}
diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h
index d798962..7cad068 100644
--- a/identity/aidl/default/libeic/EicPresentation.h
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -31,6 +31,8 @@
#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
typedef struct {
+ int featureLevel;
+
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
@@ -79,12 +81,17 @@
// SHA-256 for AdditionalData, updated for each entry.
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+ // SHA-256 of ProofOfProvisioning. Set to NUL-bytes or initialized from CredentialKeys data
+ // if credential was created with feature version 202101 or later.
+ uint8_t proofOfProvisioningSha256[EIC_SHA256_DIGEST_SIZE];
+
size_t expectedCborSizeAtEnd;
EicCbor cbor;
} EicPresentation;
bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
- const uint8_t encryptedCredentialKeys[80]);
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
uint8_t* publicKeyCert, size_t* publicKeyCertSize,
@@ -219,9 +226,19 @@
// where content is set to the ProofOfDeletion CBOR.
//
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
- size_t proofOfDeletionCborSize,
+ const uint8_t* challenge, size_t challengeSize,
+ bool includeChallenge, size_t proofOfDeletionCborSize,
uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfOwnership CBOR.
+//
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+ const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
#ifdef __cplusplus
}
#endif
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
index f16605c..3b4148e 100644
--- a/identity/aidl/default/libeic/EicProvisioning.c
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -26,10 +26,84 @@
return true;
}
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[86];
+
+ // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+ // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+ // to support loading all feature versions.
+ //
+ bool expectPopSha256 = false;
+ if (encryptedCredentialKeysSize == 52 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize == 86 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+ return false;
+ }
+
+ eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+ ctx->testCredential = testCredential;
+
+ if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+ encryptedCredentialKeysSize,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+ eicDebug("Error decrypting CredentialKeys");
+ return false;
+ }
+
+ // It's supposed to look like this;
+ //
+ // Feature version 202009:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // ]
+ //
+ // Feature version 202101:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
+ //
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+ // SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
+ eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+ eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+ // Note: We don't care about the previous ProofOfProvisioning SHA-256
+ ctx->isUpdate = true;
+ return true;
+}
+
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
size_t challengeSize, const uint8_t* applicationId,
size_t applicationIdSize, uint8_t* publicKeyCert,
size_t* publicKeyCertSize) {
+ if (ctx->isUpdate) {
+ eicDebug("Cannot create CredentialKey on update");
+ return false;
+ }
+
if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
applicationId, applicationIdSize, ctx->testCredential,
publicKeyCert, publicKeyCertSize)) {
@@ -96,6 +170,9 @@
eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+ eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
+ eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester);
+
eicCborAppendArray(&ctx->cbor, 5);
eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
eicCborAppendString(&ctx->cbor, docType);
@@ -260,14 +337,23 @@
}
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
- uint8_t encryptedCredentialKeys[80]) {
+ uint8_t* encryptedCredentialKeys,
+ size_t* encryptedCredentialKeysSize) {
EicCbor cbor;
- uint8_t cborBuf[52];
+ uint8_t cborBuf[86];
+
+ if (*encryptedCredentialKeysSize < 86 + 28) {
+ eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
+ return false;
+ }
eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
- eicCborAppendArray(&cbor, 2);
+ eicCborAppendArray(&cbor, 3);
eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+ uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
+ eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
+ eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
if (cbor.size > sizeof(cborBuf)) {
eicDebug("Exceeded buffer size");
return false;
@@ -285,6 +371,7 @@
eicDebug("Error encrypting CredentialKeys");
return false;
}
+ *encryptedCredentialKeysSize = cbor.size + 28;
return true;
}
diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h
index 836d16e..f064787 100644
--- a/identity/aidl/default/libeic/EicProvisioning.h
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -31,7 +31,7 @@
#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
typedef struct {
- // Set by eicCreateCredentialKey.
+ // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
int numEntryCounts;
@@ -43,6 +43,7 @@
size_t curEntrySize;
size_t curEntryNumBytesReceived;
+ // Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
uint8_t storageKey[EIC_AES_128_KEY_SIZE];
size_t expectedCborSizeAtEnd;
@@ -50,13 +51,23 @@
// SHA-256 for AdditionalData, updated for each entry.
uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+ // Digester just for ProofOfProvisioning (without Sig_structure).
+ EicSha256Ctx proofOfProvisioningDigester;
+
EicCbor cbor;
bool testCredential;
+
+ // Set to true if this is an update.
+ bool isUpdate;
} EicProvisioning;
bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
+
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
size_t challengeSize, const uint8_t* applicationId,
size_t applicationIdSize, uint8_t* publicKeyCert,
@@ -107,14 +118,18 @@
// CredentialKeys = [
// bstr, ; storageKey, a 128-bit AES key
// bstr ; credentialPrivKey, the private key for credentialKey
+// bstr ; SHA-256(ProofOfProvisioning)
// ]
//
+// for feature version 202101. For feature version 202009 the third field was not present.
+//
// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
-// encoded CBOR for CredentialKeys is 52 bytes and consequently
-// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
+// encoded CBOR for CredentialKeys is 86 bytes and consequently
+// |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes.
//
bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
- uint8_t encryptedCredentialKeys[80]);
+ uint8_t* encryptedCredentialKeys,
+ size_t* encryptedCredentialKeysSize);
#ifdef __cplusplus
}