diff options
author | David Zeuthen <zeuthen@google.com> | 2020-04-28 18:54:55 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2020-04-29 14:27:18 -0400 |
commit | 28edb103343576455941f39865a587d346476e65 (patch) | |
tree | 77f28316d19fbb995b0249fe55a1f61002a8022e /identity/aidl/default/IdentityCredential.cpp | |
parent | a0796e98c298fc1fc7df88f26c0d417454c34fea (diff) |
Identity Credential: Pass additional information to HAL.
Without this extra information passed upfront it's not practical to
implement a HAL which incrementally builds up cryptographically
authenticated data.
Two new methods are added to facilitate this and the HAL version
number is bumped to 2.
Bug: 154631410
Test: atest VtsHalIdentityTargetTest
Test: atest android.security.identity.cts
Change-Id: Iff63dfa2c4485c8768e06e7f6d70e940cfc8f68e
Diffstat (limited to 'identity/aidl/default/IdentityCredential.cpp')
-rw-r--r-- | identity/aidl/default/IdentityCredential.cpp | 137 |
1 files changed, 132 insertions, 5 deletions
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp index aaae1f6ae5..ff4107a7b4 100644 --- a/identity/aidl/default/IdentityCredential.cpp +++ b/identity/aidl/default/IdentityCredential.cpp @@ -25,6 +25,7 @@ #include <string.h> #include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <cppbor.h> #include <cppbor_parse.h> @@ -32,6 +33,7 @@ namespace aidl::android::hardware::identity { using ::aidl::android::hardware::keymaster::Timestamp; +using ::android::base::StringPrintf; using ::std::optional; using namespace ::android::hardware::identity; @@ -253,6 +255,12 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile, return true; } +ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces( + const vector<RequestNamespace>& requestNamespaces) { + requestNamespaces_ = requestNamespaces; + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus IdentityCredential::startRetrieval( const vector<SecureAccessControlProfile>& accessControlProfiles, const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest, @@ -447,7 +455,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( "Type mismatch in nameSpaces map")); } string requestedNamespace = nsKey->value(); - vector<string> requestedKeys; + set<string> requestedKeys; for (size_t m = 0; m < nsInnerMap->size(); m++) { const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m]; const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr(); @@ -459,13 +467,13 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE, "Type mismatch in value in nameSpaces map")); } - requestedKeys.push_back(nameItem->value()); + requestedKeys.insert(nameItem->value()); } requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys; } } - // Finally, validate all the access control profiles in the requestData. + // Validate all the access control profiles in the requestData. bool haveAuthToken = (authToken.mac.size() > 0); for (const auto& profile : accessControlProfiles) { if (!secureAccessControlProfileCheckMac(profile, storageKey_)) { @@ -496,10 +504,118 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( itemsRequest_ = itemsRequest; signingKeyBlob_ = signingKeyBlob; + // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time. + expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize(); + numStartRetrievalCalls_ += 1; return ndk::ScopedAStatus::ok(); } +size_t cborNumBytesForLength(size_t length) { + if (length < 24) { + return 0; + } else if (length <= 0xff) { + return 1; + } else if (length <= 0xffff) { + return 2; + } else if (length <= 0xffffffff) { + return 4; + } + return 8; +} + +size_t cborNumBytesForTstr(const string& value) { + return 1 + cborNumBytesForLength(value.size()) + value.size(); +} + +size_t IdentityCredential::calcDeviceNameSpacesSize() { + /* + * This is how DeviceNameSpaces is defined: + * + * DeviceNameSpaces = { + * * NameSpace => DeviceSignedItems + * } + * DeviceSignedItems = { + * + DataItemName => DataItemValue + * } + * + * Namespace = tstr + * DataItemName = tstr + * DataItemValue = any + * + * This function will calculate its length using knowledge of how CBOR is + * encoded. + */ + size_t ret = 0; + size_t numNamespacesWithValues = 0; + for (const RequestNamespace& rns : requestNamespaces_) { + vector<RequestDataItem> itemsToInclude; + + for (const RequestDataItem& rdi : rns.items) { + // If we have a CBOR request message, skip if item isn't in it + if (itemsRequest_.size() > 0) { + const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName); + if (it == requestedNameSpacesAndNames_.end()) { + continue; + } + const set<string>& dataItemNames = it->second; + if (dataItemNames.find(rdi.name) == dataItemNames.end()) { + continue; + } + } + + // Access is granted if at least one of the profiles grants access. + // + // If an item is configured without any profiles, access is denied. + // + bool authorized = false; + for (auto id : rdi.accessControlProfileIds) { + auto it = profileIdToAccessCheckResult_.find(id); + if (it != profileIdToAccessCheckResult_.end()) { + int accessControlForProfile = it->second; + if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) { + authorized = true; + break; + } + } + } + if (!authorized) { + continue; + } + + itemsToInclude.push_back(rdi); + } + + // If no entries are to be in the namespace, we don't include it... + if (itemsToInclude.size() == 0) { + continue; + } + + // Key: NameSpace + ret += cborNumBytesForTstr(rns.namespaceName); + + // Value: Open the DeviceSignedItems map + ret += 1 + cborNumBytesForLength(itemsToInclude.size()); + + for (const RequestDataItem& item : itemsToInclude) { + // Key: DataItemName + ret += cborNumBytesForTstr(item.name); + + // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use + // that. + ret += item.size; + } + + numNamespacesWithValues++; + } + + // Now that we now the nunber of namespaces with values, we know how many + // bytes the DeviceNamespaces map in the beginning is going to take up. + ret += 1 + cborNumBytesForLength(numNamespacesWithValues); + + return ret; +} + ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( const string& nameSpace, const string& name, int32_t entrySize, const vector<int32_t>& accessControlProfileIds) { @@ -558,8 +674,8 @@ ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, "Name space was not requested in startRetrieval")); } - const auto& dataItemNames = it->second; - if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) { + const set<string>& dataItemNames = it->second; + if (dataItemNames.find(name) == dataItemNames.end()) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE, "Data item name in name space was not requested in startRetrieval")); @@ -653,6 +769,17 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac, } vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode(); + if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) { + LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, " + << "was expecting " << expectedDeviceNameSpacesSize_; + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, + StringPrintf( + "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd", + encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_) + .c_str())); + } + // If there's no signing key or no sessionTranscript or no reader ephemeral // public key, we return the empty MAC. optional<vector<uint8_t>> mac; |