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