• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "IdentityCredential"
18 
19 #include "IdentityCredential.h"
20 #include "IdentityCredentialStore.h"
21 #include "Util.h"
22 
23 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
24 
25 #include <string.h>
26 
27 #include <android-base/logging.h>
28 #include <android-base/stringprintf.h>
29 
30 #include <cppbor.h>
31 #include <cppbor_parse.h>
32 
33 namespace aidl::android::hardware::identity {
34 
35 using ::aidl::android::hardware::keymaster::Timestamp;
36 using ::android::base::StringPrintf;
37 using ::std::optional;
38 
39 using namespace ::android::hardware::identity;
40 
initialize()41 int IdentityCredential::initialize() {
42     if (credentialData_.size() == 0) {
43         LOG(ERROR) << "CredentialData is empty";
44         return IIdentityCredentialStore::STATUS_INVALID_DATA;
45     }
46     auto [item, _, message] = cppbor::parse(credentialData_);
47     if (item == nullptr) {
48         LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
49         return IIdentityCredentialStore::STATUS_INVALID_DATA;
50     }
51 
52     const cppbor::Array* arrayItem = item->asArray();
53     if (arrayItem == nullptr || arrayItem->size() != 3) {
54         LOG(ERROR) << "CredentialData is not an array with three elements";
55         return IIdentityCredentialStore::STATUS_INVALID_DATA;
56     }
57 
58     const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
59     const cppbor::Bool* testCredentialItem =
60             ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
61                                                     : nullptr);
62     const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
63     if (docTypeItem == nullptr || testCredentialItem == nullptr ||
64         encryptedCredentialKeysItem == nullptr) {
65         LOG(ERROR) << "CredentialData unexpected item types";
66         return IIdentityCredentialStore::STATUS_INVALID_DATA;
67     }
68 
69     docType_ = docTypeItem->value();
70     testCredential_ = testCredentialItem->value();
71 
72     vector<uint8_t> hardwareBoundKey;
73     if (testCredential_) {
74         hardwareBoundKey = support::getTestHardwareBoundKey();
75     } else {
76         hardwareBoundKey = getHardwareBoundKey();
77     }
78 
79     const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
80     const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
81     optional<vector<uint8_t>> decryptedCredentialKeys =
82             support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
83     if (!decryptedCredentialKeys) {
84         LOG(ERROR) << "Error decrypting CredentialKeys";
85         return IIdentityCredentialStore::STATUS_INVALID_DATA;
86     }
87 
88     auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
89     if (dckItem == nullptr) {
90         LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
91         return IIdentityCredentialStore::STATUS_INVALID_DATA;
92     }
93     const cppbor::Array* dckArrayItem = dckItem->asArray();
94     if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
95         LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
96         return IIdentityCredentialStore::STATUS_INVALID_DATA;
97     }
98     const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
99     const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
100     if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
101         LOG(ERROR) << "CredentialKeys unexpected item types";
102         return IIdentityCredentialStore::STATUS_INVALID_DATA;
103     }
104     storageKey_ = storageKeyItem->value();
105     credentialPrivKey_ = credentialPrivKeyItem->value();
106 
107     return IIdentityCredentialStore::STATUS_OK;
108 }
109 
deleteCredential(vector<int8_t> * outProofOfDeletionSignature)110 ndk::ScopedAStatus IdentityCredential::deleteCredential(
111         vector<int8_t>* outProofOfDeletionSignature) {
112     cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
113     vector<uint8_t> proofOfDeletion = array.encode();
114 
115     optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
116                                                                  proofOfDeletion,  // payload
117                                                                  {},               // additionalData
118                                                                  {});  // certificateChain
119     if (!signature) {
120         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
121                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
122     }
123 
124     *outProofOfDeletionSignature = byteStringToSigned(signature.value());
125     return ndk::ScopedAStatus::ok();
126 }
127 
createEphemeralKeyPair(vector<int8_t> * outKeyPair)128 ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
129     optional<vector<uint8_t>> kp = support::createEcKeyPair();
130     if (!kp) {
131         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
132                 IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
133     }
134 
135     // Stash public key of this key-pair for later check in startRetrieval().
136     optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
137     if (!publicKey) {
138         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
139                 IIdentityCredentialStore::STATUS_FAILED,
140                 "Error getting public part of ephemeral key pair"));
141     }
142     ephemeralPublicKey_ = publicKey.value();
143 
144     *outKeyPair = byteStringToSigned(kp.value());
145     return ndk::ScopedAStatus::ok();
146 }
147 
setReaderEphemeralPublicKey(const vector<int8_t> & publicKey)148 ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
149         const vector<int8_t>& publicKey) {
150     readerPublicKey_ = byteStringToUnsigned(publicKey);
151     return ndk::ScopedAStatus::ok();
152 }
153 
createAuthChallenge(int64_t * outChallenge)154 ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
155     uint64_t challenge = 0;
156     while (challenge == 0) {
157         optional<vector<uint8_t>> bytes = support::getRandom(8);
158         if (!bytes) {
159             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
160                     IIdentityCredentialStore::STATUS_FAILED,
161                     "Error getting random data for challenge"));
162         }
163 
164         challenge = 0;
165         for (size_t n = 0; n < bytes.value().size(); n++) {
166             challenge |= ((bytes.value())[n] << (n * 8));
167         }
168     }
169 
170     *outChallenge = challenge;
171     authChallenge_ = challenge;
172     return ndk::ScopedAStatus::ok();
173 }
174 
175 // TODO: this could be a lot faster if we did all the splitting and pubkey extraction
176 // ahead of time.
checkReaderAuthentication(const SecureAccessControlProfile & profile,const vector<uint8_t> & readerCertificateChain)177 bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
178                                const vector<uint8_t>& readerCertificateChain) {
179     optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
180             byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
181     if (!acpPubKey) {
182         LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
183         return false;
184     }
185 
186     optional<vector<vector<uint8_t>>> certificatesInChain =
187             support::certificateChainSplit(readerCertificateChain);
188     if (!certificatesInChain) {
189         LOG(ERROR) << "Error splitting readerCertificateChain";
190         return false;
191     }
192     for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
193         optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
194         if (!certPubKey) {
195             LOG(ERROR)
196                     << "Error extracting public key from certificate in chain presented by reader";
197             return false;
198         }
199         if (acpPubKey.value() == certPubKey.value()) {
200             return true;
201         }
202     }
203     return false;
204 }
205 
checkUserAuthentication(const SecureAccessControlProfile & profile,const VerificationToken & verificationToken,const HardwareAuthToken & authToken,uint64_t authChallenge)206 bool checkUserAuthentication(const SecureAccessControlProfile& profile,
207                              const VerificationToken& verificationToken,
208                              const HardwareAuthToken& authToken, uint64_t authChallenge) {
209     if (profile.secureUserId != authToken.userId) {
210         LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
211                    << ") differs from userId in authToken (" << authToken.userId << ")";
212         return false;
213     }
214 
215     if (verificationToken.timestamp.milliSeconds == 0) {
216         LOG(ERROR) << "VerificationToken is not set";
217         return false;
218     }
219     if (authToken.timestamp.milliSeconds == 0) {
220         LOG(ERROR) << "AuthToken is not set";
221         return false;
222     }
223 
224     if (profile.timeoutMillis == 0) {
225         if (authToken.challenge == 0) {
226             LOG(ERROR) << "No challenge in authToken";
227             return false;
228         }
229 
230         if (authToken.challenge != int64_t(authChallenge)) {
231             LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
232                        << "doesn't match the challenge we created (" << authChallenge << ")";
233             return false;
234         }
235         return true;
236     }
237 
238     // Timeout-based user auth follows. The verification token conveys what the
239     // time is right now in the environment which generated the auth token. This
240     // is what makes it possible to do timeout-based checks.
241     //
242     const Timestamp now = verificationToken.timestamp;
243     if (authToken.timestamp.milliSeconds > now.milliSeconds) {
244         LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
245                    << ") is in the future (now: " << now.milliSeconds << ")";
246         return false;
247     }
248     if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
249         LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
250                    << profile.timeoutMillis << " = "
251                    << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
252                    << ") is in the past (now: " << now.milliSeconds << ")";
253         return false;
254     }
255     return true;
256 }
257 
setRequestedNamespaces(const vector<RequestNamespace> & requestNamespaces)258 ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
259         const vector<RequestNamespace>& requestNamespaces) {
260     requestNamespaces_ = requestNamespaces;
261     return ndk::ScopedAStatus::ok();
262 }
263 
setVerificationToken(const VerificationToken & verificationToken)264 ndk::ScopedAStatus IdentityCredential::setVerificationToken(
265         const VerificationToken& verificationToken) {
266     verificationToken_ = verificationToken;
267     return ndk::ScopedAStatus::ok();
268 }
269 
startRetrieval(const vector<SecureAccessControlProfile> & accessControlProfiles,const HardwareAuthToken & authToken,const vector<int8_t> & itemsRequestS,const vector<int8_t> & signingKeyBlobS,const vector<int8_t> & sessionTranscriptS,const vector<int8_t> & readerSignatureS,const vector<int32_t> & requestCounts)270 ndk::ScopedAStatus IdentityCredential::startRetrieval(
271         const vector<SecureAccessControlProfile>& accessControlProfiles,
272         const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
273         const vector<int8_t>& signingKeyBlobS, const vector<int8_t>& sessionTranscriptS,
274         const vector<int8_t>& readerSignatureS, const vector<int32_t>& requestCounts) {
275     auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
276     auto itemsRequest = byteStringToUnsigned(itemsRequestS);
277     auto readerSignature = byteStringToUnsigned(readerSignatureS);
278 
279     std::unique_ptr<cppbor::Item> sessionTranscriptItem;
280     if (sessionTranscript.size() > 0) {
281         auto [item, _, message] = cppbor::parse(sessionTranscript);
282         if (item == nullptr) {
283             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
284                     IIdentityCredentialStore::STATUS_INVALID_DATA,
285                     "SessionTranscript contains invalid CBOR"));
286         }
287         sessionTranscriptItem = std::move(item);
288     }
289     if (numStartRetrievalCalls_ > 0) {
290         if (sessionTranscript_ != sessionTranscript) {
291             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
292                     IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
293                     "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
294         }
295     }
296     sessionTranscript_ = sessionTranscript;
297 
298     // If there is a signature, validate that it was made with the top-most key in the
299     // certificate chain embedded in the COSE_Sign1 structure.
300     optional<vector<uint8_t>> readerCertificateChain;
301     if (readerSignature.size() > 0) {
302         readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
303         if (!readerCertificateChain) {
304             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
305                     IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
306                     "Unable to get reader certificate chain from COSE_Sign1"));
307         }
308 
309         if (!support::certificateChainValidate(readerCertificateChain.value())) {
310             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
311                     IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
312                     "Error validating reader certificate chain"));
313         }
314 
315         optional<vector<uint8_t>> readerPublicKey =
316                 support::certificateChainGetTopMostKey(readerCertificateChain.value());
317         if (!readerPublicKey) {
318             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
319                     IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
320                     "Unable to get public key from reader certificate chain"));
321         }
322 
323         const vector<uint8_t>& itemsRequestBytes = itemsRequest;
324         vector<uint8_t> encodedReaderAuthentication =
325                 cppbor::Array()
326                         .add("ReaderAuthentication")
327                         .add(std::move(sessionTranscriptItem))
328                         .add(cppbor::Semantic(24, itemsRequestBytes))
329                         .encode();
330         vector<uint8_t> encodedReaderAuthenticationBytes =
331                 cppbor::Semantic(24, encodedReaderAuthentication).encode();
332         if (!support::coseCheckEcDsaSignature(readerSignature,
333                                               encodedReaderAuthenticationBytes,  // detached content
334                                               readerPublicKey.value())) {
335             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
336                     IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
337                     "readerSignature check failed"));
338         }
339     }
340 
341     // Here's where we would validate the passed-in |authToken| to assure ourselves
342     // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
343     //
344     // However this involves calculating the MAC. However this requires access
345     // to the key needed to a pre-shared key which we don't have...
346     //
347 
348     // To prevent replay-attacks, we check that the public part of the ephemeral
349     // key we previously created, is present in the DeviceEngagement part of
350     // SessionTranscript as a COSE_Key, in uncompressed form.
351     //
352     // We do this by just searching for the X and Y coordinates.
353     if (sessionTranscript.size() > 0) {
354         auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
355         if (!getXYSuccess) {
356             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
357                     IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
358                     "Error extracting X and Y from ePub"));
359         }
360         if (sessionTranscript.size() > 0 &&
361             !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
362                      ePubX.size()) != nullptr &&
363               memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
364                      ePubY.size()) != nullptr)) {
365             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
366                     IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
367                     "Did not find ephemeral public key's X and Y coordinates in "
368                     "SessionTranscript (make sure leading zeroes are not used)"));
369         }
370     }
371 
372     // itemsRequest: If non-empty, contains request data that may be signed by the
373     // reader.  The content can be defined in the way appropriate for the
374     // credential, but there are three requirements that must be met to work with
375     // this HAL:
376     if (itemsRequest.size() > 0) {
377         // 1. The content must be a CBOR-encoded structure.
378         auto [item, _, message] = cppbor::parse(itemsRequest);
379         if (item == nullptr) {
380             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
381                     IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
382                     "Error decoding CBOR in itemsRequest"));
383         }
384 
385         // 2. The CBOR structure must be a map.
386         const cppbor::Map* map = item->asMap();
387         if (map == nullptr) {
388             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
389                     IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
390                     "itemsRequest is not a CBOR map"));
391         }
392 
393         // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
394         //    the example below.
395         //
396         //   NameSpaces = {
397         //     + NameSpace => DataElements ; Requested data elements for each NameSpace
398         //   }
399         //
400         //   NameSpace = tstr
401         //
402         //   DataElements = {
403         //     + DataElement => IntentToRetain
404         //   }
405         //
406         //   DataElement = tstr
407         //   IntentToRetain = bool
408         //
409         // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
410         // through 3.:
411         //
412         //    {
413         //        'docType' : 'org.iso.18013-5.2019',
414         //        'nameSpaces' : {
415         //            'org.iso.18013-5.2019' : {
416         //                'Last name' : false,
417         //                'Birth date' : false,
418         //                'First name' : false,
419         //                'Home address' : true
420         //            },
421         //            'org.aamva.iso.18013-5.2019' : {
422         //                'Real Id' : false
423         //            }
424         //        }
425         //    }
426         //
427         const cppbor::Map* nsMap = nullptr;
428         for (size_t n = 0; n < map->size(); n++) {
429             const auto& [keyItem, valueItem] = (*map)[n];
430             if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
431                 valueItem->type() == cppbor::MAP) {
432                 nsMap = valueItem->asMap();
433                 break;
434             }
435         }
436         if (nsMap == nullptr) {
437             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
438                     IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
439                     "No nameSpaces map in top-most map"));
440         }
441 
442         for (size_t n = 0; n < nsMap->size(); n++) {
443             auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
444             const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
445             const cppbor::Map* nsInnerMap = nsValueItem->asMap();
446             if (nsKey == nullptr || nsInnerMap == nullptr) {
447                 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
448                         IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
449                         "Type mismatch in nameSpaces map"));
450             }
451             string requestedNamespace = nsKey->value();
452             set<string> requestedKeys;
453             for (size_t m = 0; m < nsInnerMap->size(); m++) {
454                 const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
455                 const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
456                 const cppbor::Simple* simple = innerMapValueItem->asSimple();
457                 const cppbor::Bool* intentToRetainItem =
458                         (simple != nullptr) ? simple->asBool() : nullptr;
459                 if (nameItem == nullptr || intentToRetainItem == nullptr) {
460                     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
461                             IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
462                             "Type mismatch in value in nameSpaces map"));
463                 }
464                 requestedKeys.insert(nameItem->value());
465             }
466             requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
467         }
468     }
469 
470     // Validate all the access control profiles in the requestData.
471     bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
472     for (const auto& profile : accessControlProfiles) {
473         if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
474             LOG(ERROR) << "Error checking MAC for profile";
475             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
476                     IIdentityCredentialStore::STATUS_INVALID_DATA,
477                     "Error checking MAC for profile"));
478         }
479         int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
480         if (profile.userAuthenticationRequired) {
481             if (!haveAuthToken ||
482                 !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) {
483                 accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
484             }
485         } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
486             if (!readerCertificateChain ||
487                 !checkReaderAuthentication(profile, readerCertificateChain.value())) {
488                 accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
489             }
490         }
491         profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
492     }
493 
494     deviceNameSpacesMap_ = cppbor::Map();
495     currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
496 
497     requestCountsRemaining_ = requestCounts;
498     currentNameSpace_ = "";
499 
500     itemsRequest_ = itemsRequest;
501     signingKeyBlob_ = byteStringToUnsigned(signingKeyBlobS);
502 
503     // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time.
504     expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize();
505 
506     numStartRetrievalCalls_ += 1;
507     return ndk::ScopedAStatus::ok();
508 }
509 
cborNumBytesForLength(size_t length)510 size_t cborNumBytesForLength(size_t length) {
511     if (length < 24) {
512         return 0;
513     } else if (length <= 0xff) {
514         return 1;
515     } else if (length <= 0xffff) {
516         return 2;
517     } else if (length <= 0xffffffff) {
518         return 4;
519     }
520     return 8;
521 }
522 
cborNumBytesForTstr(const string & value)523 size_t cborNumBytesForTstr(const string& value) {
524     return 1 + cborNumBytesForLength(value.size()) + value.size();
525 }
526 
calcDeviceNameSpacesSize()527 size_t IdentityCredential::calcDeviceNameSpacesSize() {
528     /*
529      * This is how DeviceNameSpaces is defined:
530      *
531      *        DeviceNameSpaces = {
532      *            * NameSpace => DeviceSignedItems
533      *        }
534      *        DeviceSignedItems = {
535      *            + DataItemName => DataItemValue
536      *        }
537      *
538      *        Namespace = tstr
539      *        DataItemName = tstr
540      *        DataItemValue = any
541      *
542      * This function will calculate its length using knowledge of how CBOR is
543      * encoded.
544      */
545     size_t ret = 0;
546     size_t numNamespacesWithValues = 0;
547     for (const RequestNamespace& rns : requestNamespaces_) {
548         vector<RequestDataItem> itemsToInclude;
549 
550         for (const RequestDataItem& rdi : rns.items) {
551             // If we have a CBOR request message, skip if item isn't in it
552             if (itemsRequest_.size() > 0) {
553                 const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
554                 if (it == requestedNameSpacesAndNames_.end()) {
555                     continue;
556                 }
557                 const set<string>& dataItemNames = it->second;
558                 if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
559                     continue;
560                 }
561             }
562 
563             // Access is granted if at least one of the profiles grants access.
564             //
565             // If an item is configured without any profiles, access is denied.
566             //
567             bool authorized = false;
568             for (auto id : rdi.accessControlProfileIds) {
569                 auto it = profileIdToAccessCheckResult_.find(id);
570                 if (it != profileIdToAccessCheckResult_.end()) {
571                     int accessControlForProfile = it->second;
572                     if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
573                         authorized = true;
574                         break;
575                     }
576                 }
577             }
578             if (!authorized) {
579                 continue;
580             }
581 
582             itemsToInclude.push_back(rdi);
583         }
584 
585         // If no entries are to be in the namespace, we don't include it...
586         if (itemsToInclude.size() == 0) {
587             continue;
588         }
589 
590         // Key: NameSpace
591         ret += cborNumBytesForTstr(rns.namespaceName);
592 
593         // Value: Open the DeviceSignedItems map
594         ret += 1 + cborNumBytesForLength(itemsToInclude.size());
595 
596         for (const RequestDataItem& item : itemsToInclude) {
597             // Key: DataItemName
598             ret += cborNumBytesForTstr(item.name);
599 
600             // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use
601             // that.
602             ret += item.size;
603         }
604 
605         numNamespacesWithValues++;
606     }
607 
608     // Now that we now the nunber of namespaces with values, we know how many
609     // bytes the DeviceNamespaces map in the beginning is going to take up.
610     ret += 1 + cborNumBytesForLength(numNamespacesWithValues);
611 
612     return ret;
613 }
614 
startRetrieveEntryValue(const string & nameSpace,const string & name,int32_t entrySize,const vector<int32_t> & accessControlProfileIds)615 ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
616         const string& nameSpace, const string& name, int32_t entrySize,
617         const vector<int32_t>& accessControlProfileIds) {
618     if (name.empty()) {
619         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
620                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
621     }
622     if (nameSpace.empty()) {
623         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
624                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
625     }
626 
627     if (requestCountsRemaining_.size() == 0) {
628         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
629                 IIdentityCredentialStore::STATUS_INVALID_DATA,
630                 "No more name spaces left to go through"));
631     }
632 
633     if (currentNameSpace_ == "") {
634         // First call.
635         currentNameSpace_ = nameSpace;
636     }
637 
638     if (nameSpace == currentNameSpace_) {
639         // Same namespace.
640         if (requestCountsRemaining_[0] == 0) {
641             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
642                     IIdentityCredentialStore::STATUS_INVALID_DATA,
643                     "No more entries to be retrieved in current name space"));
644         }
645         requestCountsRemaining_[0] -= 1;
646     } else {
647         // New namespace.
648         if (requestCountsRemaining_[0] != 0) {
649             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
650                     IIdentityCredentialStore::STATUS_INVALID_DATA,
651                     "Moved to new name space but one or more entries need to be retrieved "
652                     "in current name space"));
653         }
654         if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
655             deviceNameSpacesMap_.add(currentNameSpace_,
656                                      std::move(currentNameSpaceDeviceNameSpacesMap_));
657         }
658         currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
659 
660         requestCountsRemaining_.erase(requestCountsRemaining_.begin());
661         currentNameSpace_ = nameSpace;
662     }
663 
664     // It's permissible to have an empty itemsRequest... but if non-empty you can
665     // only request what was specified in said itemsRequest. Enforce that.
666     if (itemsRequest_.size() > 0) {
667         const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
668         if (it == requestedNameSpacesAndNames_.end()) {
669             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
670                     IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
671                     "Name space was not requested in startRetrieval"));
672         }
673         const set<string>& dataItemNames = it->second;
674         if (dataItemNames.find(name) == dataItemNames.end()) {
675             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
676                     IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
677                     "Data item name in name space was not requested in startRetrieval"));
678         }
679     }
680 
681     // Enforce access control.
682     //
683     // Access is granted if at least one of the profiles grants access.
684     //
685     // If an item is configured without any profiles, access is denied.
686     //
687     int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
688     for (auto id : accessControlProfileIds) {
689         auto search = profileIdToAccessCheckResult_.find(id);
690         if (search == profileIdToAccessCheckResult_.end()) {
691             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
692                     IIdentityCredentialStore::STATUS_INVALID_DATA,
693                     "Requested entry with unvalidated profile id"));
694         }
695         int accessControlForProfile = search->second;
696         if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
697             accessControl = IIdentityCredentialStore::STATUS_OK;
698             break;
699         }
700         accessControl = accessControlForProfile;
701     }
702     if (accessControl != IIdentityCredentialStore::STATUS_OK) {
703         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
704                 int(accessControl), "Access control check failed"));
705     }
706 
707     entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
708 
709     currentName_ = name;
710     entryRemainingBytes_ = entrySize;
711     entryValue_.resize(0);
712 
713     return ndk::ScopedAStatus::ok();
714 }
715 
retrieveEntryValue(const vector<int8_t> & encryptedContentS,vector<int8_t> * outContent)716 ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
717                                                           vector<int8_t>* outContent) {
718     auto encryptedContent = byteStringToUnsigned(encryptedContentS);
719     optional<vector<uint8_t>> content =
720             support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
721     if (!content) {
722         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
723                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
724     }
725 
726     size_t chunkSize = content.value().size();
727 
728     if (chunkSize > entryRemainingBytes_) {
729         LOG(ERROR) << "Retrieved chunk of size " << chunkSize
730                    << " is bigger than remaining space of size " << entryRemainingBytes_;
731         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
732                 IIdentityCredentialStore::STATUS_INVALID_DATA,
733                 "Retrieved chunk is bigger than remaining space"));
734     }
735 
736     entryRemainingBytes_ -= chunkSize;
737     if (entryRemainingBytes_ > 0) {
738         if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
739             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
740                     IIdentityCredentialStore::STATUS_INVALID_DATA,
741                     "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
742         }
743     }
744 
745     entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
746 
747     if (entryRemainingBytes_ == 0) {
748         auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
749         if (entryValueItem == nullptr) {
750             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
751                     IIdentityCredentialStore::STATUS_INVALID_DATA,
752                     "Retrieved data which is invalid CBOR"));
753         }
754         currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
755     }
756 
757     *outContent = byteStringToSigned(content.value());
758     return ndk::ScopedAStatus::ok();
759 }
760 
finishRetrieval(vector<int8_t> * outMac,vector<int8_t> * outDeviceNameSpaces)761 ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<int8_t>* outMac,
762                                                        vector<int8_t>* outDeviceNameSpaces) {
763     if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
764         deviceNameSpacesMap_.add(currentNameSpace_,
765                                  std::move(currentNameSpaceDeviceNameSpacesMap_));
766     }
767     vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
768 
769     if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
770         LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, "
771                    << "was expecting " << expectedDeviceNameSpacesSize_;
772         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
773                 IIdentityCredentialStore::STATUS_INVALID_DATA,
774                 StringPrintf(
775                         "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd",
776                         encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_)
777                         .c_str()));
778     }
779 
780     // If there's no signing key or no sessionTranscript or no reader ephemeral
781     // public key, we return the empty MAC.
782     optional<vector<uint8_t>> mac;
783     if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
784         readerPublicKey_.size() > 0) {
785         vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
786         optional<vector<uint8_t>> signingKey =
787                 support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
788         if (!signingKey) {
789             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
790                     IIdentityCredentialStore::STATUS_INVALID_DATA,
791                     "Error decrypting signingKeyBlob"));
792         }
793 
794         vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
795         optional<vector<uint8_t>> eMacKey =
796                 support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
797         if (!eMacKey) {
798             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
799                     IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
800         }
801         mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
802                                eMacKey.value());
803         if (!mac) {
804             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
805                     IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
806         }
807     }
808 
809     *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
810     *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
811     return ndk::ScopedAStatus::ok();
812 }
813 
generateSigningKeyPair(vector<int8_t> * outSigningKeyBlob,Certificate * outSigningKeyCertificate)814 ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
815         vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
816     string serialDecimal = "1";
817     string issuer = "Android Identity Credential Key";
818     string subject = "Android Identity Credential Authentication Key";
819     time_t validityNotBefore = time(nullptr);
820     time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
821 
822     optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
823     if (!signingKeyPKCS8) {
824         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
825                 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
826     }
827 
828     optional<vector<uint8_t>> signingPublicKey =
829             support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
830     if (!signingPublicKey) {
831         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
832                 IIdentityCredentialStore::STATUS_FAILED,
833                 "Error getting public part of signingKey"));
834     }
835 
836     optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
837     if (!signingKey) {
838         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
839                 IIdentityCredentialStore::STATUS_FAILED,
840                 "Error getting private part of signingKey"));
841     }
842 
843     optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
844             signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
845             validityNotBefore, validityNotAfter);
846     if (!certificate) {
847         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
848                 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
849     }
850 
851     optional<vector<uint8_t>> nonce = support::getRandom(12);
852     if (!nonce) {
853         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
854                 IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
855     }
856     vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
857     optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
858             storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
859     if (!encryptedSigningKey) {
860         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
861                 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
862     }
863     *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
864     *outSigningKeyCertificate = Certificate();
865     outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
866     return ndk::ScopedAStatus::ok();
867 }
868 
869 }  // namespace aidl::android::hardware::identity
870