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 "WritableIdentityCredential"
18 
19 #include "WritableIdentityCredential.h"
20 #include "IdentityCredentialStore.h"
21 
22 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
23 
24 #include <android-base/logging.h>
25 #include <android-base/stringprintf.h>
26 
27 #include <cppbor/cppbor.h>
28 #include <cppbor/cppbor_parse.h>
29 
30 #include <utility>
31 
32 #include "IdentityCredentialStore.h"
33 #include "Util.h"
34 #include "WritableIdentityCredential.h"
35 
36 namespace aidl::android::hardware::identity {
37 
38 using ::android::base::StringPrintf;
39 using ::std::optional;
40 using namespace ::android::hardware::identity;
41 
initialize()42 bool WritableIdentityCredential::initialize() {
43     optional<vector<uint8_t>> random = support::getRandom(16);
44     if (!random) {
45         LOG(ERROR) << "Error creating storageKey";
46         return false;
47     }
48     storageKey_ = random.value();
49     startPersonalizationCalled_ = false;
50     firstEntry_ = true;
51 
52     return true;
53 }
54 
55 // This function generates the attestation certificate using the passed in
56 // |attestationApplicationId| and |attestationChallenge|.  It will generate an
57 // attestation certificate with current time and expires one year from now.  The
58 // certificate shall contain all values as specified in hal.
getAttestationCertificate(const vector<int8_t> & attestationApplicationId,const vector<int8_t> & attestationChallenge,vector<Certificate> * outCertificateChain)59 ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
60         const vector<int8_t>& attestationApplicationId,  //
61         const vector<int8_t>& attestationChallenge,      //
62         vector<Certificate>* outCertificateChain) {
63     if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
64         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
65                 IIdentityCredentialStore::STATUS_FAILED,
66                 "Error attestation certificate previously generated"));
67     }
68     if (attestationChallenge.empty()) {
69         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
70                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
71     }
72 
73     vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
74     vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
75 
76     optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
77             support::createEcKeyPairAndAttestation(challenge, appId);
78     if (!keyAttestationPair) {
79         LOG(ERROR) << "Error creating credentialKey and attestation";
80         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
81                 IIdentityCredentialStore::STATUS_FAILED,
82                 "Error creating credentialKey and attestation"));
83     }
84 
85     vector<uint8_t> keyPair = keyAttestationPair.value().first;
86     certificateChain_ = keyAttestationPair.value().second;
87 
88     optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
89     if (!pubKey) {
90         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
91                 IIdentityCredentialStore::STATUS_FAILED,
92                 "Error getting public part of credentialKey"));
93     }
94     credentialPubKey_ = pubKey.value();
95 
96     optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
97     if (!privKey) {
98         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
99                 IIdentityCredentialStore::STATUS_FAILED,
100                 "Error getting private part of credentialKey"));
101     }
102     credentialPrivKey_ = privKey.value();
103 
104     // convert from vector<vector<uint8_t>>> to vector<Certificate>*
105     *outCertificateChain = vector<Certificate>();
106     for (const vector<uint8_t>& cert : certificateChain_) {
107         Certificate c = Certificate();
108         c.encodedCertificate = byteStringToSigned(cert);
109         outCertificateChain->push_back(std::move(c));
110     }
111     return ndk::ScopedAStatus::ok();
112 }
113 
setExpectedProofOfProvisioningSize(int32_t expectedProofOfProvisioningSize)114 ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
115         int32_t expectedProofOfProvisioningSize) {
116     expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
117     return ndk::ScopedAStatus::ok();
118 }
119 
startPersonalization(int32_t accessControlProfileCount,const vector<int32_t> & entryCounts)120 ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
121         int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
122     if (startPersonalizationCalled_) {
123         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
124                 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
125     }
126 
127     startPersonalizationCalled_ = true;
128     numAccessControlProfileRemaining_ = accessControlProfileCount;
129     remainingEntryCounts_ = entryCounts;
130     entryNameSpace_ = "";
131 
132     signedDataAccessControlProfiles_ = cppbor::Array();
133     signedDataNamespaces_ = cppbor::Map();
134     signedDataCurrentNamespace_ = cppbor::Array();
135 
136     return ndk::ScopedAStatus::ok();
137 }
138 
addAccessControlProfile(int32_t id,const Certificate & readerCertificate,bool userAuthenticationRequired,int64_t timeoutMillis,int64_t secureUserId,SecureAccessControlProfile * outSecureAccessControlProfile)139 ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
140         int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
141         int64_t timeoutMillis, int64_t secureUserId,
142         SecureAccessControlProfile* outSecureAccessControlProfile) {
143     SecureAccessControlProfile profile;
144 
145     if (numAccessControlProfileRemaining_ == 0) {
146         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
147                 IIdentityCredentialStore::STATUS_INVALID_DATA,
148                 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
149     }
150 
151     if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
152         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
153                 IIdentityCredentialStore::STATUS_INVALID_DATA,
154                 "Access Control Profile id must be unique"));
155     }
156     accessControlProfileIds_.insert(id);
157 
158     if (id < 0 || id >= 32) {
159         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
160                 IIdentityCredentialStore::STATUS_INVALID_DATA,
161                 "Access Control Profile id must be non-negative and less than 32"));
162     }
163 
164     // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
165     // be zero.
166     if (!userAuthenticationRequired && timeoutMillis != 0) {
167         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
168                 IIdentityCredentialStore::STATUS_INVALID_DATA,
169                 "userAuthenticationRequired is false but timeout is non-zero"));
170     }
171 
172     // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
173     if (userAuthenticationRequired && secureUserId == 0) {
174         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
175                 IIdentityCredentialStore::STATUS_INVALID_DATA,
176                 "userAuthenticationRequired is true but secureUserId is zero"));
177     }
178 
179     profile.id = id;
180     profile.readerCertificate = readerCertificate;
181     profile.userAuthenticationRequired = userAuthenticationRequired;
182     profile.timeoutMillis = timeoutMillis;
183     profile.secureUserId = secureUserId;
184     optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
185     if (!mac) {
186         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
187                 IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
188     }
189     profile.mac = byteStringToSigned(mac.value());
190 
191     cppbor::Map profileMap;
192     profileMap.add("id", profile.id);
193     if (profile.readerCertificate.encodedCertificate.size() > 0) {
194         profileMap.add(
195                 "readerCertificate",
196                 cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
197     }
198     if (profile.userAuthenticationRequired) {
199         profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
200         profileMap.add("timeoutMillis", profile.timeoutMillis);
201     }
202     signedDataAccessControlProfiles_.add(std::move(profileMap));
203 
204     numAccessControlProfileRemaining_--;
205 
206     *outSecureAccessControlProfile = profile;
207     return ndk::ScopedAStatus::ok();
208 }
209 
beginAddEntry(const vector<int32_t> & accessControlProfileIds,const string & nameSpace,const string & name,int32_t entrySize)210 ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
211         const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
212         int32_t entrySize) {
213     if (numAccessControlProfileRemaining_ != 0) {
214         LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
215                    << " and expected zero";
216         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
217                 IIdentityCredentialStore::STATUS_INVALID_DATA,
218                 "numAccessControlProfileRemaining_ is not zero"));
219     }
220 
221     if (remainingEntryCounts_.size() == 0) {
222         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
223                 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
224     }
225 
226     // Handle initial beginEntry() call.
227     if (firstEntry_) {
228         firstEntry_ = false;
229         entryNameSpace_ = nameSpace;
230         allNameSpaces_.insert(nameSpace);
231     }
232 
233     // If the namespace changed...
234     if (nameSpace != entryNameSpace_) {
235         if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
236             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
237                     IIdentityCredentialStore::STATUS_INVALID_DATA,
238                     "Name space cannot be added in interleaving fashion"));
239         }
240 
241         // Then check that all entries in the previous namespace have been added..
242         if (remainingEntryCounts_[0] != 0) {
243             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
244                     IIdentityCredentialStore::STATUS_INVALID_DATA,
245                     "New namespace but a non-zero number of entries remain to be added"));
246         }
247         remainingEntryCounts_.erase(remainingEntryCounts_.begin());
248         remainingEntryCounts_[0] -= 1;
249         allNameSpaces_.insert(nameSpace);
250 
251         if (signedDataCurrentNamespace_.size() > 0) {
252             signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
253             signedDataCurrentNamespace_ = cppbor::Array();
254         }
255     } else {
256         // Same namespace...
257         if (remainingEntryCounts_[0] == 0) {
258             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
259                     IIdentityCredentialStore::STATUS_INVALID_DATA,
260                     "Same namespace but no entries remain to be added"));
261         }
262         remainingEntryCounts_[0] -= 1;
263     }
264 
265     entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
266 
267     entryRemainingBytes_ = entrySize;
268     entryNameSpace_ = nameSpace;
269     entryName_ = name;
270     entryAccessControlProfileIds_ = accessControlProfileIds;
271     entryBytes_.resize(0);
272     // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
273     return ndk::ScopedAStatus::ok();
274 }
275 
addEntryValue(const vector<int8_t> & contentS,vector<int8_t> * outEncryptedContentS)276 ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
277                                                              vector<int8_t>* outEncryptedContentS) {
278     auto content = byteStringToUnsigned(contentS);
279     size_t contentSize = content.size();
280 
281     if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
282         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
283                 IIdentityCredentialStore::STATUS_INVALID_DATA,
284                 "Passed in chunk of is bigger than kGcmChunkSize"));
285     }
286     if (contentSize > entryRemainingBytes_) {
287         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
288                 IIdentityCredentialStore::STATUS_INVALID_DATA,
289                 "Passed in chunk is bigger than remaining space"));
290     }
291 
292     entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
293     entryRemainingBytes_ -= contentSize;
294     if (entryRemainingBytes_ > 0) {
295         if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
296             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
297                     IIdentityCredentialStore::STATUS_INVALID_DATA,
298                     "Retrieved non-final chunk which isn't kGcmChunkSize"));
299         }
300     }
301 
302     optional<vector<uint8_t>> nonce = support::getRandom(12);
303     if (!nonce) {
304         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
305                 IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
306     }
307     optional<vector<uint8_t>> encryptedContent =
308             support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
309     if (!encryptedContent) {
310         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
311                 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
312     }
313 
314     if (entryRemainingBytes_ == 0) {
315         // TODO: ideally do do this without parsing the data (but still validate data is valid
316         // CBOR).
317         auto [item, _, message] = cppbor::parse(entryBytes_);
318         if (item == nullptr) {
319             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
320                     IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
321         }
322         cppbor::Map entryMap;
323         entryMap.add("name", entryName_);
324         entryMap.add("value", std::move(item));
325         cppbor::Array profileIdArray;
326         for (auto id : entryAccessControlProfileIds_) {
327             profileIdArray.add(id);
328         }
329         entryMap.add("accessControlProfiles", std::move(profileIdArray));
330         signedDataCurrentNamespace_.add(std::move(entryMap));
331     }
332 
333     *outEncryptedContentS = byteStringToSigned(encryptedContent.value());
334     return ndk::ScopedAStatus::ok();
335 }
336 
337 // Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
338 // |credentialPrivKey|.
generateCredentialKeys(const vector<uint8_t> & storageKey,const vector<uint8_t> & credentialPrivKey,vector<uint8_t> & credentialKeys)339 static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
340                                    const vector<uint8_t>& credentialPrivKey,
341                                    vector<uint8_t>& credentialKeys) {
342     if (storageKey.size() != 16) {
343         LOG(ERROR) << "Size of storageKey is not 16";
344         return false;
345     }
346 
347     cppbor::Array array;
348     array.add(cppbor::Bstr(storageKey));
349     array.add(cppbor::Bstr(credentialPrivKey));
350     credentialKeys = array.encode();
351     return true;
352 }
353 
354 // Writes CBOR-encoded structure to |credentialData| containing |docType|,
355 // |testCredential| and |credentialKeys|. The latter element will be stored in
356 // encrypted form, using |hardwareBoundKey| as the encryption key.
generateCredentialData(const vector<uint8_t> & hardwareBoundKey,const string & docType,bool testCredential,const vector<uint8_t> & credentialKeys,vector<uint8_t> & credentialData)357 bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
358                             bool testCredential, const vector<uint8_t>& credentialKeys,
359                             vector<uint8_t>& credentialData) {
360     optional<vector<uint8_t>> nonce = support::getRandom(12);
361     if (!nonce) {
362         LOG(ERROR) << "Error getting random";
363         return false;
364     }
365     vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
366     optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
367             hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
368     if (!credentialBlob) {
369         LOG(ERROR) << "Error encrypting CredentialKeys blob";
370         return false;
371     }
372 
373     cppbor::Array array;
374     array.add(docType);
375     array.add(testCredential);
376     array.add(cppbor::Bstr(credentialBlob.value()));
377     credentialData = array.encode();
378     return true;
379 }
380 
finishAddingEntries(vector<int8_t> * outCredentialData,vector<int8_t> * outProofOfProvisioningSignature)381 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
382         vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
383     if (numAccessControlProfileRemaining_ != 0) {
384         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
385                 IIdentityCredentialStore::STATUS_INVALID_DATA,
386                 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
387     }
388 
389     if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
390         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
391                 IIdentityCredentialStore::STATUS_INVALID_DATA,
392                 "More entry spaces remain than startPersonalization configured"));
393     }
394 
395     if (signedDataCurrentNamespace_.size() > 0) {
396         signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
397     }
398     cppbor::Array popArray;
399     popArray.add("ProofOfProvisioning")
400             .add(docType_)
401             .add(std::move(signedDataAccessControlProfiles_))
402             .add(std::move(signedDataNamespaces_))
403             .add(testCredential_);
404     vector<uint8_t> encodedCbor = popArray.encode();
405 
406     if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
407         LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
408                    << "was expecting " << expectedProofOfProvisioningSize_;
409         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
410                 IIdentityCredentialStore::STATUS_INVALID_DATA,
411                 StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
412                              encodedCbor.size(), expectedProofOfProvisioningSize_)
413                         .c_str()));
414     }
415 
416     optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
417                                                                  encodedCbor,  // payload
418                                                                  {},           // additionalData
419                                                                  {});          // certificateChain
420     if (!signature) {
421         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
422                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
423     }
424 
425     vector<uint8_t> credentialKeys;
426     if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
427         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
428                 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
429     }
430 
431     vector<uint8_t> credentialData;
432     if (!generateCredentialData(
433                 testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
434                 docType_, testCredential_, credentialKeys, credentialData)) {
435         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
436                 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
437     }
438 
439     *outCredentialData = byteStringToSigned(credentialData);
440     *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
441     return ndk::ScopedAStatus::ok();
442 }
443 
444 }  // namespace aidl::android::hardware::identity
445