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
This commit is contained in:
David Zeuthen 2020-04-28 18:54:55 -04:00
parent a0796e98c2
commit 28edb10334
27 changed files with 622 additions and 23 deletions

View file

@ -18,5 +18,8 @@ aidl_interface {
},
},
},
versions: ["1"],
versions: [
"1",
"2",
],
}

View file

@ -0,0 +1 @@
3b0b10b618dbc4bf283aa2bf78833ad3de0a5928

View file

@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable Certificate {
byte[] encodedCertificate;
}

View file

@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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;
@Backing(type="int") @VintfStability
enum CipherSuite {
CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1,
}

View file

@ -0,0 +1,26 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable HardwareInformation {
@utf8InCpp String credentialStoreName;
@utf8InCpp String credentialStoreAuthorName;
int dataChunkSize;
boolean isDirectAccess;
@utf8InCpp String[] supportedDocTypes;
}

View file

@ -0,0 +1,31 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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 IIdentityCredential {
byte[] deleteCredential();
byte[] createEphemeralKeyPair();
void setReaderEphemeralPublicKey(in byte[] publicKey);
long createAuthChallenge();
void startRetrieval(in android.hardware.identity.SecureAccessControlProfile[] accessControlProfiles, in android.hardware.keymaster.HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob, in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize, in int[] accessControlProfileIds);
byte[] retrieveEntryValue(in byte[] encryptedContent);
void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
}

View file

@ -0,0 +1,37 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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 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);
const int STATUS_OK = 0;
const int STATUS_FAILED = 1;
const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
const int STATUS_INVALID_DATA = 3;
const int STATUS_INVALID_AUTH_TOKEN = 4;
const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5;
const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6;
const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7;
const int STATUS_USER_AUTHENTICATION_FAILED = 8;
const int STATUS_READER_AUTHENTICATION_FAILED = 9;
const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10;
const int STATUS_NOT_IN_REQUEST_MESSAGE = 11;
const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12;
}

View file

@ -0,0 +1,28 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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 IWritableIdentityCredential {
android.hardware.identity.Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
void startPersonalization(in int accessControlProfileCount, in int[] entryCounts);
android.hardware.identity.SecureAccessControlProfile addAccessControlProfile(in int id, in android.hardware.identity.Certificate readerCertificate, in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize);
byte[] addEntryValue(in byte[] content);
void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
}

View file

@ -0,0 +1,24 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable RequestDataItem {
@utf8InCpp String name;
long size;
int[] accessControlProfileIds;
}

View file

@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable RequestNamespace {
@utf8InCpp String namespaceName;
android.hardware.identity.RequestDataItem[] items;
}

View file

@ -0,0 +1,27 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable SecureAccessControlProfile {
int id;
android.hardware.identity.Certificate readerCertificate;
boolean userAuthenticationRequired;
long timeoutMillis;
long secureUserId;
byte[] mac;
}

View file

@ -27,4 +27,5 @@ interface IIdentityCredential {
byte[] retrieveEntryValue(in byte[] encryptedContent);
void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
}

View file

@ -24,4 +24,5 @@ interface IWritableIdentityCredential {
void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize);
byte[] addEntryValue(in byte[] content);
void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
}

View file

@ -0,0 +1,24 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable RequestDataItem {
@utf8InCpp String name;
long size;
int[] accessControlProfileIds;
}

View file

@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
// edit this file. It looks like you are doing that because you have modified
// an AIDL interface in a backward-incompatible way, e.g., deleting a function
// from an interface or a field from a parcelable and it broke the build. That
// breakage is intended.
//
// You must not make a backward incompatible changes to the AIDL files 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
parcelable RequestNamespace {
@utf8InCpp String namespaceName;
android.hardware.identity.RequestDataItem[] items;
}

View file

@ -17,6 +17,7 @@
package android.hardware.identity;
import android.hardware.identity.Certificate;
import android.hardware.identity.RequestNamespace;
import android.hardware.identity.SecureAccessControlProfile;
import android.hardware.keymaster.HardwareAuthToken;
@ -82,6 +83,8 @@ interface IIdentityCredential {
/**
* Start an entry retrieval process.
*
* The setRequestedNamespaces() method will be called before this method.
*
* This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
* createAuthChallenge() and before startRetrieveEntry(). This method call is followed by
* multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally
@ -274,7 +277,7 @@ interface IIdentityCredential {
* "DeviceAuthentication",
* SessionTranscript,
* DocType,
* DeviceNameSpaceBytes,
* DeviceNameSpacesBytes,
* ]
*
* DocType = tstr
@ -343,4 +346,16 @@ interface IIdentityCredential {
* @return an X.509 certificate for the new signing key, signed by the credential key.
*/
Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
/**
* Sets the namespaces and data items (including their size and access control profiles)
* which will be requested. This method must be called before startRetrieval() is called.
*
* This information is provided to make it possible for a HAL implementation to
* incrementally build up cryptographically authenticated data which includes the
* DeviceNameSpaces CBOR.
*
* @param requestNamespaces Namespaces and data items which will be requested.
*/
void setRequestedNamespaces(in RequestNamespace[] requestNamespaces);
}

View file

@ -120,6 +120,8 @@ interface IWritableIdentityCredential {
*
* startPersonalization must not be called more than once.
*
* The setExpectedProofOfProvisioningSize() method will be called before this method.
*
* @param accessControlProfileCount specifies the number of access control profiles that will
* be provisioned with addAccessControlProfile().
*
@ -288,4 +290,16 @@ interface IWritableIdentityCredential {
*/
void finishAddingEntries(out byte[] credentialData,
out byte[] proofOfProvisioningSignature);
/**
* Sets the expected size of the ProofOfProvisioning returned by finishAddingEntries(). This
* method must be called before startPersonalization() is called.
*
* This information is provided to make it possible for a HAL implementation to
* incrementally build up cryptographically authenticated data which includes the
* ProofOfProvisioning CBOR.
*
* @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning.
*/
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
}

View file

@ -0,0 +1,38 @@
/*
* 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.
*/
package android.hardware.identity;
@VintfStability
parcelable RequestDataItem {
/**
* The data item name being requested, for example "driving_privileges".
*/
@utf8InCpp String name;
/**
* The size of the data item value.
*
* Data item values are always encoded as CBOR so this is the length of
* the CBOR encoding of the value.
*/
long size;
/**
* The access control profile ids this data item is configured with.
*/
int[] accessControlProfileIds;
}

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
package android.hardware.identity;
import android.hardware.identity.RequestDataItem;
@VintfStability
parcelable RequestNamespace {
/**
* The name of the namespace that items are being requested from, for
* example "org.iso.18013.5.1".
*/
@utf8InCpp String namespaceName;
/**
* The data items requested.
*/
RequestDataItem[] items;
}

View file

@ -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;

View file

@ -32,15 +32,17 @@ namespace aidl::android::hardware::identity {
using ::aidl::android::hardware::keymaster::HardwareAuthToken;
using ::std::map;
using ::std::set;
using ::std::string;
using ::std::vector;
using MapStringToVectorOfStrings = map<string, vector<string>>;
class IdentityCredential : public BnIdentityCredential {
public:
IdentityCredential(const vector<uint8_t>& credentialData)
: credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
: credentialData_(credentialData),
numStartRetrievalCalls_(0),
authChallenge_(0),
expectedDeviceNameSpacesSize_(0) {}
// Parses and decrypts credentialData_, return a status code from
// IIdentityCredentialStore. Must be called right after construction.
@ -51,6 +53,8 @@ class IdentityCredential : public BnIdentityCredential {
ndk::ScopedAStatus createEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
ndk::ScopedAStatus setRequestedNamespaces(
const vector<RequestNamespace>& requestNamespaces) override;
ndk::ScopedAStatus startRetrieval(
const vector<SecureAccessControlProfile>& accessControlProfiles,
const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
@ -86,6 +90,9 @@ class IdentityCredential : public BnIdentityCredential {
// Set by createAuthChallenge()
uint64_t authChallenge_;
// Set by setRequestedNamespaces()
vector<RequestNamespace> requestNamespaces_;
// Set at startRetrieval() time.
map<int32_t, int> profileIdToAccessCheckResult_;
vector<uint8_t> signingKeyBlob_;
@ -93,16 +100,21 @@ class IdentityCredential : public BnIdentityCredential {
std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
vector<uint8_t> itemsRequest_;
vector<int32_t> requestCountsRemaining_;
MapStringToVectorOfStrings requestedNameSpacesAndNames_;
map<string, set<string>> requestedNameSpacesAndNames_;
cppbor::Map deviceNameSpacesMap_;
cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
// Calculated at startRetrieval() time.
size_t expectedDeviceNameSpacesSize_;
// Set at startRetrieveEntryValue() time.
string currentNameSpace_;
string currentName_;
size_t entryRemainingBytes_;
vector<uint8_t> entryValue_;
vector<uint8_t> entryAdditionalData_;
size_t calcDeviceNameSpacesSize();
};
} // namespace aidl::android::hardware::identity

View file

@ -22,6 +22,7 @@
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cppbor/cppbor.h>
#include <cppbor/cppbor_parse.h>
@ -34,6 +35,7 @@
namespace aidl::android::hardware::identity {
using ::android::base::StringPrintf;
using ::std::optional;
using namespace ::android::hardware::identity;
@ -105,6 +107,12 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
int32_t expectedProofOfProvisioningSize) {
expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
if (startPersonalizationCalled_) {
@ -382,6 +390,16 @@ ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
.add(testCredential_);
vector<uint8_t> encodedCbor = popArray.encode();
if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
<< "was expecting " << expectedProofOfProvisioningSize_;
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,
StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
encodedCbor.size(), expectedProofOfProvisioningSize_)
.c_str()));
}
optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
encodedCbor, // payload
{}, // additionalData

View file

@ -43,6 +43,9 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
const vector<uint8_t>& attestationChallenge,
vector<Certificate>* outCertificateChain) override;
ndk::ScopedAStatus setExpectedProofOfProvisioningSize(
int32_t expectedProofOfProvisioningSize) override;
ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
const vector<int32_t>& entryCounts) override;
@ -62,7 +65,7 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
vector<uint8_t>* outCredentialData,
vector<uint8_t>* outProofOfProvisioningSignature) override;
// private:
private:
string docType_;
bool testCredential_;
@ -82,6 +85,7 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
cppbor::Array signedDataAccessControlProfiles_;
cppbor::Map signedDataNamespaces_;
cppbor::Array signedDataCurrentNamespace_;
size_t expectedProofOfProvisioningSize_;
// This field is initialized in addAccessControlProfile
set<int32_t> accessControlProfileIds_;

View file

@ -112,6 +112,11 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
// TODO: set it to something random and check it's in the cert chain
ASSERT_GE(attData.attestationCertificate.size(), 2);
// This is kinda of a hack but we need to give the size of
// ProofOfProvisioning that we'll expect to receive.
const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
ASSERT_TRUE(
writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
.isOk());
@ -268,6 +273,8 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
ASSERT_TRUE(credential->setRequestedNamespaces(requestedNamespaces).isOk());
ASSERT_TRUE(credential
->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
signingKeyBlob, sessionTranscriptBytes,

View file

@ -279,14 +279,17 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
EXPECT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
const vector<int32_t> entryCounts = {1u};
writableCredential->startPersonalization(1, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
ASSERT_TRUE(readerCertificate1);
const vector<int32_t> entryCounts = {1u};
size_t expectedPoPSize = 186 + readerCertificate1.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
result = writableCredential->startPersonalization(1, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
const vector<test_utils::TestProfile> testProfiles = {{1, readerCertificate1.value(), true, 1}};
optional<vector<SecureAccessControlProfile>> secureProfiles =
@ -374,7 +377,11 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) {
{2, readerCertificate2.value(), true, 2},
};
const vector<int32_t> entryCounts = {1u, 3u, 1u, 1u, 2u};
writableCredential->startPersonalization(testProfiles.size(), entryCounts);
size_t expectedPoPSize =
525021 + readerCertificate1.value().size() + readerCertificate2.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
result = writableCredential->startPersonalization(testProfiles.size(), entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@ -518,11 +525,6 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) {
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
const vector<int32_t> entryCounts = {2u, 2u};
writableCredential->startPersonalization(3, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
ASSERT_TRUE(readerCertificate1);
@ -530,6 +532,16 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) {
test_utils::GenerateReaderCertificate("123456987987987987987987");
ASSERT_TRUE(readerCertificate2);
const vector<int32_t> entryCounts = {2u, 2u};
size_t expectedPoPSize =
377 + readerCertificate1.value().size() + readerCertificate2.value().size();
;
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
writableCredential->startPersonalization(3, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
const vector<test_utils::TestProfile> testProfiles = {{0, readerCertificate1.value(), false, 0},
{1, readerCertificate2.value(), true, 1},
{2, {}, false, 0}};

View file

@ -176,4 +176,28 @@ void SetImageData(vector<uint8_t>& image) {
}
}
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
vector<RequestNamespace> ret;
RequestNamespace curNs;
for (const TestEntryData& testEntry : entries) {
if (testEntry.nameSpace != curNs.namespaceName) {
if (curNs.namespaceName.size() > 0) {
ret.push_back(curNs);
}
curNs.namespaceName = testEntry.nameSpace;
curNs.items.clear();
}
RequestDataItem item;
item.name = testEntry.name;
item.size = testEntry.valueCbor.size();
item.accessControlProfileIds = testEntry.profileIds;
curNs.items.push_back(item);
}
if (curNs.namespaceName.size() > 0) {
ret.push_back(curNs);
}
return ret;
}
} // namespace android::hardware::identity::test_utils

View file

@ -113,6 +113,8 @@ bool ValidateAttestationCertificate(vector<Certificate>& inputCertificates);
void SetImageData(vector<uint8_t>& image);
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
} // namespace android::hardware::identity::test_utils
#endif // VTS_IDENTITY_TEST_UTILS_H