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