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 hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
84 if (!certChain) {
85 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
86 IIdentityCredentialStore::STATUS_FAILED,
87 "Error generating attestation certificate chain"));
88 }
89
90 optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
91 if (!certs) {
92 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
93 IIdentityCredentialStore::STATUS_FAILED,
94 "Error splitting chain into separate certificates"));
95 }
96
97 *outCertificateChain = vector<Certificate>();
98 for (const vector<uint8_t>& cert : certs.value()) {
99 Certificate c = Certificate();
100 c.encodedCertificate = cert;
101 outCertificateChain->push_back(std::move(c));
102 }
103
104 return ndk::ScopedAStatus::ok();
105 }
106
setExpectedProofOfProvisioningSize(int32_t expectedProofOfProvisioningSize)107 ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
108 int32_t expectedProofOfProvisioningSize) {
109 expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
110 return ndk::ScopedAStatus::ok();
111 }
112
startPersonalization(int32_t accessControlProfileCount,const vector<int32_t> & entryCounts)113 ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
114 int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
115 if (startPersonalizationCalled_) {
116 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
117 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
118 }
119 startPersonalizationCalled_ = true;
120
121 numAccessControlProfileRemaining_ = accessControlProfileCount;
122 remainingEntryCounts_ = entryCounts;
123 entryNameSpace_ = "";
124
125 signedDataAccessControlProfiles_ = cppbor::Array();
126 signedDataNamespaces_ = cppbor::Map();
127 signedDataCurrentNamespace_ = cppbor::Array();
128
129 if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
130 expectedProofOfProvisioningSize_)) {
131 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
132 IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
133 }
134
135 return ndk::ScopedAStatus::ok();
136 }
137
addAccessControlProfile(int32_t id,const Certificate & readerCertificate,bool userAuthenticationRequired,int64_t timeoutMillis,int64_t secureUserId,SecureAccessControlProfile * outSecureAccessControlProfile)138 ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
139 int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
140 int64_t timeoutMillis, int64_t secureUserId,
141 SecureAccessControlProfile* outSecureAccessControlProfile) {
142 if (numAccessControlProfileRemaining_ == 0) {
143 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
144 IIdentityCredentialStore::STATUS_INVALID_DATA,
145 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
146 }
147
148 if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
149 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
150 IIdentityCredentialStore::STATUS_INVALID_DATA,
151 "Access Control Profile id must be unique"));
152 }
153 accessControlProfileIds_.insert(id);
154
155 if (id < 0 || id >= 32) {
156 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
157 IIdentityCredentialStore::STATUS_INVALID_DATA,
158 "Access Control Profile id must be non-negative and less than 32"));
159 }
160
161 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
162 // be zero.
163 if (!userAuthenticationRequired && timeoutMillis != 0) {
164 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
165 IIdentityCredentialStore::STATUS_INVALID_DATA,
166 "userAuthenticationRequired is false but timeout is non-zero"));
167 }
168
169 optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
170 id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
171 secureUserId);
172 if (!mac) {
173 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
174 IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
175 }
176
177 SecureAccessControlProfile profile;
178 profile.id = id;
179 profile.readerCertificate = readerCertificate;
180 profile.userAuthenticationRequired = userAuthenticationRequired;
181 profile.timeoutMillis = timeoutMillis;
182 profile.secureUserId = secureUserId;
183 profile.mac = mac.value();
184 cppbor::Map profileMap;
185 profileMap.add("id", profile.id);
186 if (profile.readerCertificate.encodedCertificate.size() > 0) {
187 profileMap.add("readerCertificate",
188 cppbor::Bstr(profile.readerCertificate.encodedCertificate));
189 }
190 if (profile.userAuthenticationRequired) {
191 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
192 profileMap.add("timeoutMillis", profile.timeoutMillis);
193 }
194 signedDataAccessControlProfiles_.add(std::move(profileMap));
195
196 numAccessControlProfileRemaining_--;
197
198 *outSecureAccessControlProfile = profile;
199 return ndk::ScopedAStatus::ok();
200 }
201
beginAddEntry(const vector<int32_t> & accessControlProfileIds,const string & nameSpace,const string & name,int32_t entrySize)202 ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
203 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
204 int32_t entrySize) {
205 if (numAccessControlProfileRemaining_ != 0) {
206 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
207 << " and expected zero";
208 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
209 IIdentityCredentialStore::STATUS_INVALID_DATA,
210 "numAccessControlProfileRemaining_ is not zero"));
211 }
212
213 // Ensure passed-in profile ids reference valid access control profiles
214 for (const int32_t id : accessControlProfileIds) {
215 if (accessControlProfileIds_.find(id) == accessControlProfileIds_.end()) {
216 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
217 IIdentityCredentialStore::STATUS_INVALID_DATA,
218 "An id in accessControlProfileIds references non-existing ACP"));
219 }
220 }
221
222 if (remainingEntryCounts_.size() == 0) {
223 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
224 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
225 }
226
227 // Handle initial beginEntry() call.
228 if (firstEntry_) {
229 firstEntry_ = false;
230 entryNameSpace_ = nameSpace;
231 allNameSpaces_.insert(nameSpace);
232 }
233
234 // If the namespace changed...
235 if (nameSpace != entryNameSpace_) {
236 if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
237 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
238 IIdentityCredentialStore::STATUS_INVALID_DATA,
239 "Name space cannot be added in interleaving fashion"));
240 }
241
242 // Then check that all entries in the previous namespace have been added..
243 if (remainingEntryCounts_[0] != 0) {
244 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
245 IIdentityCredentialStore::STATUS_INVALID_DATA,
246 "New namespace but a non-zero number of entries remain to be added"));
247 }
248 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
249 remainingEntryCounts_[0] -= 1;
250 allNameSpaces_.insert(nameSpace);
251
252 if (signedDataCurrentNamespace_.size() > 0) {
253 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
254 signedDataCurrentNamespace_ = cppbor::Array();
255 }
256 } else {
257 // Same namespace...
258 if (remainingEntryCounts_[0] == 0) {
259 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
260 IIdentityCredentialStore::STATUS_INVALID_DATA,
261 "Same namespace but no entries remain to be added"));
262 }
263 remainingEntryCounts_[0] -= 1;
264 }
265
266 entryRemainingBytes_ = entrySize;
267 entryNameSpace_ = nameSpace;
268 entryName_ = name;
269 entryAccessControlProfileIds_ = accessControlProfileIds;
270 entryBytes_.resize(0);
271 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
272
273 if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
274 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
275 IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
276 }
277
278 return ndk::ScopedAStatus::ok();
279 }
280
addEntryValue(const vector<uint8_t> & content,vector<uint8_t> * outEncryptedContent)281 ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
282 vector<uint8_t>* outEncryptedContent) {
283 size_t contentSize = content.size();
284
285 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
286 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
287 IIdentityCredentialStore::STATUS_INVALID_DATA,
288 "Passed in chunk of is bigger than kGcmChunkSize"));
289 }
290 if (contentSize > entryRemainingBytes_) {
291 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
292 IIdentityCredentialStore::STATUS_INVALID_DATA,
293 "Passed in chunk is bigger than remaining space"));
294 }
295
296 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
297 entryRemainingBytes_ -= contentSize;
298 if (entryRemainingBytes_ > 0) {
299 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
300 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
301 IIdentityCredentialStore::STATUS_INVALID_DATA,
302 "Retrieved non-final chunk which isn't kGcmChunkSize"));
303 }
304 }
305
306 optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
307 entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
308 if (!encryptedContent) {
309 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
310 IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
311 }
312
313 if (entryRemainingBytes_ == 0) {
314 // TODO: ideally do do this without parsing the data (but still validate data is valid
315 // CBOR).
316 auto [item, _, message] = cppbor::parse(entryBytes_);
317 if (item == nullptr) {
318 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
319 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
320 }
321 cppbor::Map entryMap;
322 entryMap.add("name", entryName_);
323 entryMap.add("value", std::move(item));
324 cppbor::Array profileIdArray;
325 for (auto id : entryAccessControlProfileIds_) {
326 profileIdArray.add(id);
327 }
328 entryMap.add("accessControlProfiles", std::move(profileIdArray));
329 signedDataCurrentNamespace_.add(std::move(entryMap));
330 }
331
332 *outEncryptedContent = encryptedContent.value();
333 return ndk::ScopedAStatus::ok();
334 }
335
finishAddingEntries(vector<uint8_t> * outCredentialData,vector<uint8_t> * outProofOfProvisioningSignature)336 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
337 vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
338 if (numAccessControlProfileRemaining_ != 0) {
339 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
340 IIdentityCredentialStore::STATUS_INVALID_DATA,
341 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
342 }
343
344 if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
345 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
346 IIdentityCredentialStore::STATUS_INVALID_DATA,
347 "More entry spaces remain than startPersonalization configured"));
348 }
349
350 if (signedDataCurrentNamespace_.size() > 0) {
351 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
352 }
353 cppbor::Array popArray;
354 popArray.add("ProofOfProvisioning")
355 .add(docType_)
356 .add(std::move(signedDataAccessControlProfiles_))
357 .add(std::move(signedDataNamespaces_))
358 .add(testCredential_);
359 vector<uint8_t> encodedCbor = popArray.encode();
360
361 if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
362 LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
363 << "was expecting " << expectedProofOfProvisioningSize_;
364 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
365 IIdentityCredentialStore::STATUS_INVALID_DATA,
366 StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
367 encodedCbor.size(), expectedProofOfProvisioningSize_)
368 .c_str()));
369 }
370
371 optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
372 if (!signatureOfToBeSigned) {
373 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
374 IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
375 }
376
377 optional<vector<uint8_t>> signature =
378 support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
379 encodedCbor, // data
380 {}); // certificateChain
381 if (!signature) {
382 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
383 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
384 }
385
386 optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
387 if (!encryptedCredentialKeys) {
388 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
389 IIdentityCredentialStore::STATUS_FAILED,
390 "Error generating encrypted CredentialKeys"));
391 }
392 cppbor::Array array;
393 array.add(docType_);
394 array.add(testCredential_);
395 array.add(encryptedCredentialKeys.value());
396 vector<uint8_t> credentialData = array.encode();
397
398 *outCredentialData = credentialData;
399 *outProofOfProvisioningSignature = signature.value();
400 hwProxy_->shutdown();
401
402 return ndk::ScopedAStatus::ok();
403 }
404
405 } // namespace aidl::android::hardware::identity
406