Identity: Move signingKeyBlob from finishRetrieval() to startRetrieval().

The implementation of the Identity Credential TA in constrained
environments may need to incrementally update the HMAC-SHA256 of
DeviceAuthencation CBOR to avoid keeping the entire CBOR structure in
memory. To do this they need to calculate the derived key before
starting to build the CBOR so they need access to the signingKey
earlier on.

Bug: 150390415
Test: atest android.security.identity.cts
Test: VtsHalIdentityTargetTest
Merged-In: I72ad30ec3ccec0b8161cbea360ef8c9212f8cbbc
Change-Id: I95e28dd46b35bc31dec8d77ee14b5a1b3b5c0391
This commit is contained in:
David Zeuthen 2020-02-27 14:25:54 -05:00
parent c5a652431f
commit b790d97f45
4 changed files with 25 additions and 26 deletions

View file

@ -176,6 +176,10 @@ interface IIdentityCredential {
* @param itemsRequest
* If non-empty, contains request data that is signed by the reader. See above.
*
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
* below) containing the signing key to use to sign the data retrieved. If this
* is not in the right format the call fails with STATUS_INVALID_DATA.
*
* @param sessionTranscript
* Either empty or the CBOR of the SessionTranscript. See above.
*
@ -195,8 +199,7 @@ interface IIdentityCredential {
* and remove the corresponding requests from the counts.
*/
void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles,
in HardwareAuthToken authToken,
in byte[] itemsRequest,
in HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob,
in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
/**
@ -254,10 +257,6 @@ interface IIdentityCredential {
* If signingKeyBlob or the sessionTranscript parameter passed to startRetrieval() is
* empty then the returned MAC will be empty.
*
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
* below) containing the signing key to use to sign the data retrieved. If this
* is not in the right format the call fails with STATUS_INVALID_DATA.
*
* @param out mac is empty if signingKeyBlob or the sessionTranscript passed to
* startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
* and the detached content is set to DeviceAuthentication as defined below.
@ -304,7 +303,7 @@ interface IIdentityCredential {
*
* @param out deviceNameSpaces the bytes of DeviceNameSpaces.
*/
void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces);
void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
/**
* Generate a key pair to be used for signing session data and retrieved data items.

View file

@ -256,8 +256,8 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile,
ndk::ScopedAStatus IdentityCredential::startRetrieval(
const vector<SecureAccessControlProfile>& accessControlProfiles,
const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS,
const vector<int32_t>& requestCounts) {
const vector<int8_t>& signingKeyBlobS, const vector<int8_t>& sessionTranscriptS,
const vector<int8_t>& readerSignatureS, const vector<int32_t>& requestCounts) {
auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
auto itemsRequest = byteStringToUnsigned(itemsRequestS);
auto readerSignature = byteStringToUnsigned(readerSignatureS);
@ -498,6 +498,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
currentNameSpace_ = "";
itemsRequest_ = itemsRequest;
signingKeyBlob_ = byteStringToUnsigned(signingKeyBlobS);
numStartRetrievalCalls_ += 1;
return ndk::ScopedAStatus::ok();
@ -650,11 +651,8 @@ ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>&
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS,
vector<int8_t>* outMac,
ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<int8_t>* outMac,
vector<int8_t>* outDeviceNameSpaces) {
auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS);
if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
deviceNameSpacesMap_.add(currentNameSpace_,
std::move(currentNameSpaceDeviceNameSpacesMap_));
@ -664,7 +662,8 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& sig
// 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;
if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
readerPublicKey_.size() > 0) {
cppbor::Array array;
array.add("DeviceAuthentication");
array.add(sessionTranscriptItem_->clone());
@ -674,7 +673,7 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& sig
vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
optional<vector<uint8_t>> signingKey =
support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
if (!signingKey) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,

View file

@ -54,14 +54,14 @@ class IdentityCredential : public BnIdentityCredential {
ndk::ScopedAStatus startRetrieval(
const vector<SecureAccessControlProfile>& accessControlProfiles,
const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature,
const vector<int32_t>& requestCounts) override;
const vector<int8_t>& signingKeyBlob, const vector<int8_t>& sessionTranscript,
const vector<int8_t>& readerSignature, const vector<int32_t>& requestCounts) override;
ndk::ScopedAStatus startRetrieveEntryValue(
const string& nameSpace, const string& name, int32_t entrySize,
const vector<int32_t>& accessControlProfileIds) override;
ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
vector<int8_t>* outContent) override;
ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac,
ndk::ScopedAStatus finishRetrieval(vector<int8_t>* outMac,
vector<int8_t>* outDeviceNameSpaces) override;
ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
Certificate* outSigningKeyCertificate) override;
@ -88,6 +88,7 @@ class IdentityCredential : public BnIdentityCredential {
// Set at startRetrieval() time.
map<int32_t, int> profileIdToAccessCheckResult_;
vector<uint8_t> signingKeyBlob_;
vector<uint8_t> sessionTranscript_;
std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
vector<uint8_t> itemsRequest_;

View file

@ -352,10 +352,15 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
readerCertificate.value());
ASSERT_TRUE(readerSignature);
// Generate the key that will be used to sign AuthenticatedData.
vector<uint8_t> signingKeyBlob;
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
ASSERT_TRUE(credential
->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
sessionTranscriptBytes, readerSignature.value(),
testEntriesEntryCounts)
signingKeyBlob, sessionTranscriptBytes,
readerSignature.value(), testEntriesEntryCounts)
.isOk());
for (const auto& entry : testEntries) {
@ -377,14 +382,9 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
EXPECT_EQ(content, entry.valueCbor);
}
// Generate the key that will be used to sign AuthenticatedData.
vector<uint8_t> signingKeyBlob;
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
vector<uint8_t> mac;
vector<uint8_t> deviceNameSpacesBytes;
ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
ASSERT_EQ(
"{\n"