credstore: Pass additional information to Identity Credential HAL.

Without this extra information passed upfront it's not practical to
implement a HAL which incrementally builds up cryptographically
authenticated data.

This information is conveyed by using two new methods on version 2 of
the Identity Credential HAL. If these methods are not implemented (if
a version 1 HAL is running) the invocation fails and we handle this
gracefully by just ignoring the error.

Bug: 154631410
Test: atest VtsHalIdentityTargetTest
Test: atest android.security.identity.cts

Change-Id: I17d516e41e800f58daa4c11dcca0305c80740d5b
This commit is contained in:
David Zeuthen 2020-04-27 13:34:38 -04:00
parent da132924a0
commit e2a78a48c0
4 changed files with 115 additions and 5 deletions

View file

@ -261,7 +261,35 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
signingKeyBlob = authKey->keyBlob;
}
Status status =
// Pass the HAL enough information to allow calculating the size of
// DeviceNameSpaces ahead of time.
vector<RequestNamespace> halRequestNamespaces;
for (const RequestNamespaceParcel& rns : requestNamespaces) {
RequestNamespace ns;
ns.namespaceName = rns.namespaceName;
for (const RequestEntryParcel& rep : rns.entries) {
optional<EntryData> entryData = data_->getEntryData(rns.namespaceName, rep.name);
if (entryData) {
RequestDataItem di;
di.name = rep.name;
di.size = entryData.value().size;
di.accessControlProfileIds = entryData.value().accessControlProfileIds;
ns.items.push_back(di);
}
}
if (ns.items.size() > 0) {
halRequestNamespaces.push_back(ns);
}
}
// This is not catastrophic, we might be dealing with a version 1 implementation which
// doesn't have this method.
Status status = halBinder_->setRequestedNamespaces(halRequestNamespaces);
if (!status.isOk()) {
LOG(INFO) << "Failed setting expected requested namespaces assuming V1 HAL "
<< "and continuing";
}
status =
halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob,
sessionTranscript, readerSignature, requestCounts);
if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {

View file

@ -38,6 +38,8 @@ using ::std::vector;
using ::android::hardware::identity::CipherSuite;
using ::android::hardware::identity::IIdentityCredential;
using ::android::hardware::identity::IIdentityCredentialStore;
using ::android::hardware::identity::RequestDataItem;
using ::android::hardware::identity::RequestNamespace;
class Credential : public BnCredential {
public:
@ -80,6 +82,11 @@ class Credential : public BnCredential {
sp<CredentialData> data_;
sp<IIdentityCredential> halBinder_;
ssize_t
calcExpectedDeviceNameSpacesSize(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
uint32_t authorizedAcps);
};
} // namespace identity

View file

@ -39,10 +39,10 @@ using ::android::hardware::identity::SecureAccessControlProfile;
using ::android::hardware::identity::support::chunkVector;
WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
const string& /*docType*/, size_t dataChunkSize,
const string& docType, size_t dataChunkSize,
sp<IWritableIdentityCredential> halBinder)
: dataPath_(dataPath), credentialName_(credentialName), dataChunkSize_(dataChunkSize),
halBinder_(halBinder) {}
: dataPath_(dataPath), credentialName_(credentialName), docType_(docType),
dataChunkSize_(dataChunkSize), halBinder_(halBinder) {}
WritableCredential::~WritableCredential() {}
@ -89,6 +89,62 @@ Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t
return Status::ok();
}
ssize_t WritableCredential::calcExpectedProofOfProvisioningSize(
const vector<AccessControlProfileParcel>& accessControlProfiles,
const vector<EntryNamespaceParcel>& entryNamespaces) {
// Right now, we calculate the size by simply just calculating the
// CBOR. There's a little bit of overhead associated with this (as compared
// to just adding up sizes) but it's a lot simpler and robust. In the future
// if this turns out to be a problem, we can optimize it.
//
cppbor::Array acpArray;
for (const AccessControlProfileParcel& profile : accessControlProfiles) {
cppbor::Map map;
map.add("id", profile.id);
if (profile.readerCertificate.size() > 0) {
map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
}
if (profile.userAuthenticationRequired) {
map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
map.add("timeoutMillis", profile.userAuthenticationTimeoutMillis);
}
acpArray.add(std::move(map));
}
cppbor::Map dataMap;
for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
cppbor::Array entriesArray;
for (const EntryParcel& eParcel : ensParcel.entries) {
// TODO: ideally do do this without parsing the data (but still validate data is valid
// CBOR).
auto [itemForValue, _, _2] = cppbor::parse(eParcel.value);
if (itemForValue == nullptr) {
return -1;
}
cppbor::Map entryMap;
entryMap.add("name", eParcel.name);
entryMap.add("value", std::move(itemForValue));
cppbor::Array acpIdsArray;
for (int32_t id : eParcel.accessControlProfileIds) {
acpIdsArray.add(id);
}
entryMap.add("accessControlProfiles", std::move(acpIdsArray));
entriesArray.add(std::move(entryMap));
}
dataMap.add(ensParcel.namespaceName, std::move(entriesArray));
}
cppbor::Array array;
array.add("ProofOfProvisioning");
array.add(docType_);
array.add(std::move(acpArray));
array.add(std::move(dataMap));
array.add(false); // testCredential
return array.encode().size();
}
Status
WritableCredential::personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
const vector<EntryNamespaceParcel>& entryNamespaces,
@ -113,7 +169,21 @@ WritableCredential::personalize(const vector<AccessControlProfileParcel>& access
entryCounts.push_back(ensParcel.entries.size());
}
Status status = halBinder_->startPersonalization(accessControlProfiles.size(), entryCounts);
ssize_t expectedPoPSize =
calcExpectedProofOfProvisioningSize(accessControlProfiles, entryNamespaces);
if (expectedPoPSize < 0) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Data is not valid CBOR");
}
// This is not catastrophic, we might be dealing with a version 1 implementation which
// doesn't have this method.
Status status = halBinder_->setExpectedProofOfProvisioningSize(expectedPoPSize);
if (!status.isOk()) {
LOG(INFO) << "Failed setting expected ProofOfProvisioning size, assuming V1 HAL "
<< "and continuing";
}
status = halBinder_->startPersonalization(accessControlProfiles.size(), entryCounts);
if (!status.isOk()) {
return halStatusToGenericError(status);
}

View file

@ -50,10 +50,15 @@ class WritableCredential : public BnWritableCredential {
private:
string dataPath_;
string credentialName_;
string docType_;
size_t dataChunkSize_;
sp<IWritableIdentityCredential> halBinder_;
vector<uint8_t> attestationCertificate_;
ssize_t calcExpectedProofOfProvisioningSize(
const vector<AccessControlProfileParcel>& accessControlProfiles,
const vector<EntryNamespaceParcel>& entryNamespaces);
Status ensureAttestationCertificateExists(const vector<uint8_t>& challenge);
};