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