blob: fd246d8fecb9f350b59f517e9909609cbf98c6ef [file] [log] [blame]
David Zeuthenc75ac312019-10-28 13:16:45 -04001/*
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
Selene Huang459cb802020-01-08 22:59:02 -080019#include "WritableIdentityCredential.h"
20#include "IdentityCredentialStore.h"
21
David Zeuthenc75ac312019-10-28 13:16:45 -040022#include <android/hardware/identity/support/IdentityCredentialSupport.h>
23
24#include <android-base/logging.h>
25
26#include <cppbor/cppbor.h>
27#include <cppbor/cppbor_parse.h>
28
Selene Huang459cb802020-01-08 22:59:02 -080029#include <utility>
30
David Zeuthen81603152020-02-11 22:04:24 -050031#include "IdentityCredentialStore.h"
32#include "Util.h"
33#include "WritableIdentityCredential.h"
34
35namespace aidl::android::hardware::identity {
David Zeuthenc75ac312019-10-28 13:16:45 -040036
37using ::std::optional;
David Zeuthen81603152020-02-11 22:04:24 -050038using namespace ::android::hardware::identity;
39
40bool WritableIdentityCredential::initialize() {
David Zeuthen81603152020-02-11 22:04:24 -050041 optional<vector<uint8_t>> random = support::getRandom(16);
42 if (!random) {
43 LOG(ERROR) << "Error creating storageKey";
44 return false;
45 }
46 storageKey_ = random.value();
Selene Huang92b61d62020-03-04 02:24:16 -080047 startPersonalizationCalled_ = false;
48 firstEntry_ = true;
David Zeuthen81603152020-02-11 22:04:24 -050049
50 return true;
51}
52
Selene Huang459cb802020-01-08 22:59:02 -080053// This function generates the attestation certificate using the passed in
54// |attestationApplicationId| and |attestationChallenge|. It will generate an
55// attestation certificate with current time and expires one year from now. The
56// certificate shall contain all values as specified in hal.
David Zeuthen81603152020-02-11 22:04:24 -050057ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
Jooyung Han17be89b2020-02-21 21:17:06 +090058 const vector<uint8_t>& attestationApplicationId, //
59 const vector<uint8_t>& attestationChallenge, //
Selene Huang459cb802020-01-08 22:59:02 -080060 vector<Certificate>* outCertificateChain) {
61 if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
David Zeuthen81603152020-02-11 22:04:24 -050062 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
63 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080064 "Error attestation certificate previously generated"));
David Zeuthen81603152020-02-11 22:04:24 -050065 }
66
Selene Huang459cb802020-01-08 22:59:02 -080067 vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
68 vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
69
70 optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
71 support::createEcKeyPairAndAttestation(challenge, appId);
72 if (!keyAttestationPair) {
73 LOG(ERROR) << "Error creating credentialKey and attestation";
David Zeuthen81603152020-02-11 22:04:24 -050074 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
75 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080076 "Error creating credentialKey and attestation"));
David Zeuthen81603152020-02-11 22:04:24 -050077 }
78
Selene Huang459cb802020-01-08 22:59:02 -080079 vector<uint8_t> keyPair = keyAttestationPair.value().first;
80 certificateChain_ = keyAttestationPair.value().second;
David Zeuthen81603152020-02-11 22:04:24 -050081
Selene Huang459cb802020-01-08 22:59:02 -080082 optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
83 if (!pubKey) {
David Zeuthen81603152020-02-11 22:04:24 -050084 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
85 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080086 "Error getting public part of credentialKey"));
David Zeuthen81603152020-02-11 22:04:24 -050087 }
Selene Huang459cb802020-01-08 22:59:02 -080088 credentialPubKey_ = pubKey.value();
David Zeuthen81603152020-02-11 22:04:24 -050089
Selene Huang459cb802020-01-08 22:59:02 -080090 optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
91 if (!privKey) {
David Zeuthen81603152020-02-11 22:04:24 -050092 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
93 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080094 "Error getting private part of credentialKey"));
David Zeuthen81603152020-02-11 22:04:24 -050095 }
Selene Huang459cb802020-01-08 22:59:02 -080096 credentialPrivKey_ = privKey.value();
David Zeuthen81603152020-02-11 22:04:24 -050097
Selene Huang459cb802020-01-08 22:59:02 -080098 // convert from vector<vector<uint8_t>>> to vector<Certificate>*
David Zeuthen81603152020-02-11 22:04:24 -050099 *outCertificateChain = vector<Certificate>();
Selene Huang459cb802020-01-08 22:59:02 -0800100 for (const vector<uint8_t>& cert : certificateChain_) {
David Zeuthen81603152020-02-11 22:04:24 -0500101 Certificate c = Certificate();
Jooyung Han17be89b2020-02-21 21:17:06 +0900102 c.encodedCertificate = cert;
David Zeuthen81603152020-02-11 22:04:24 -0500103 outCertificateChain->push_back(std::move(c));
104 }
105 return ndk::ScopedAStatus::ok();
106}
107
108ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
109 int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
Selene Huang92b61d62020-03-04 02:24:16 -0800110 if (startPersonalizationCalled_) {
111 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
112 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
113 }
114
115 startPersonalizationCalled_ = true;
David Zeuthen81603152020-02-11 22:04:24 -0500116 numAccessControlProfileRemaining_ = accessControlProfileCount;
117 remainingEntryCounts_ = entryCounts;
118 entryNameSpace_ = "";
119
120 signedDataAccessControlProfiles_ = cppbor::Array();
121 signedDataNamespaces_ = cppbor::Map();
122 signedDataCurrentNamespace_ = cppbor::Array();
123
124 return ndk::ScopedAStatus::ok();
125}
126
127ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
128 int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
129 int64_t timeoutMillis, int64_t secureUserId,
130 SecureAccessControlProfile* outSecureAccessControlProfile) {
131 SecureAccessControlProfile profile;
132
133 if (numAccessControlProfileRemaining_ == 0) {
134 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
135 IIdentityCredentialStore::STATUS_INVALID_DATA,
136 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
137 }
138
Selene Huang92b61d62020-03-04 02:24:16 -0800139 if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
140 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
141 IIdentityCredentialStore::STATUS_INVALID_DATA,
142 "Access Control Profile id must be unique"));
143 }
144 accessControlProfileIds_.insert(id);
145
David Zeuthen81603152020-02-11 22:04:24 -0500146 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
147 // be zero.
148 if (!userAuthenticationRequired && timeoutMillis != 0) {
149 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
150 IIdentityCredentialStore::STATUS_INVALID_DATA,
151 "userAuthenticationRequired is false but timeout is non-zero"));
152 }
153
154 profile.id = id;
155 profile.readerCertificate = readerCertificate;
156 profile.userAuthenticationRequired = userAuthenticationRequired;
157 profile.timeoutMillis = timeoutMillis;
158 profile.secureUserId = secureUserId;
159 optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
160 if (!mac) {
161 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
162 IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
163 }
Jooyung Han17be89b2020-02-21 21:17:06 +0900164 profile.mac = mac.value();
David Zeuthen81603152020-02-11 22:04:24 -0500165
166 cppbor::Map profileMap;
167 profileMap.add("id", profile.id);
168 if (profile.readerCertificate.encodedCertificate.size() > 0) {
Jooyung Han17be89b2020-02-21 21:17:06 +0900169 profileMap.add("readerCertificate",
170 cppbor::Bstr(profile.readerCertificate.encodedCertificate));
David Zeuthen81603152020-02-11 22:04:24 -0500171 }
172 if (profile.userAuthenticationRequired) {
173 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
174 profileMap.add("timeoutMillis", profile.timeoutMillis);
175 }
176 signedDataAccessControlProfiles_.add(std::move(profileMap));
177
178 numAccessControlProfileRemaining_--;
179
180 *outSecureAccessControlProfile = profile;
181 return ndk::ScopedAStatus::ok();
182}
183
184ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
185 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
186 int32_t entrySize) {
187 if (numAccessControlProfileRemaining_ != 0) {
188 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
189 << " and expected zero";
190 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
191 IIdentityCredentialStore::STATUS_INVALID_DATA,
192 "numAccessControlProfileRemaining_ is not zero"));
193 }
194
195 if (remainingEntryCounts_.size() == 0) {
196 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
197 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
198 }
199
200 // Handle initial beginEntry() call.
Selene Huang92b61d62020-03-04 02:24:16 -0800201 if (firstEntry_) {
202 firstEntry_ = false;
David Zeuthen81603152020-02-11 22:04:24 -0500203 entryNameSpace_ = nameSpace;
Selene Huang92b61d62020-03-04 02:24:16 -0800204 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500205 }
206
207 // If the namespace changed...
208 if (nameSpace != entryNameSpace_) {
Selene Huang92b61d62020-03-04 02:24:16 -0800209 if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
210 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
211 IIdentityCredentialStore::STATUS_INVALID_DATA,
212 "Name space cannot be added in interleaving fashion"));
213 }
214
David Zeuthen81603152020-02-11 22:04:24 -0500215 // Then check that all entries in the previous namespace have been added..
216 if (remainingEntryCounts_[0] != 0) {
217 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
218 IIdentityCredentialStore::STATUS_INVALID_DATA,
219 "New namespace but a non-zero number of entries remain to be added"));
220 }
221 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
Selene Huang92b61d62020-03-04 02:24:16 -0800222 remainingEntryCounts_[0] -= 1;
223 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500224
225 if (signedDataCurrentNamespace_.size() > 0) {
226 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
227 signedDataCurrentNamespace_ = cppbor::Array();
228 }
229 } else {
230 // Same namespace...
231 if (remainingEntryCounts_[0] == 0) {
232 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
233 IIdentityCredentialStore::STATUS_INVALID_DATA,
234 "Same namespace but no entries remain to be added"));
235 }
236 remainingEntryCounts_[0] -= 1;
237 }
238
239 entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
240
241 entryRemainingBytes_ = entrySize;
242 entryNameSpace_ = nameSpace;
243 entryName_ = name;
244 entryAccessControlProfileIds_ = accessControlProfileIds;
245 entryBytes_.resize(0);
246 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
247 return ndk::ScopedAStatus::ok();
248}
249
Jooyung Han17be89b2020-02-21 21:17:06 +0900250ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
251 vector<uint8_t>* outEncryptedContent) {
David Zeuthen81603152020-02-11 22:04:24 -0500252 size_t contentSize = content.size();
253
254 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
255 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
256 IIdentityCredentialStore::STATUS_INVALID_DATA,
257 "Passed in chunk of is bigger than kGcmChunkSize"));
258 }
259 if (contentSize > entryRemainingBytes_) {
260 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
261 IIdentityCredentialStore::STATUS_INVALID_DATA,
262 "Passed in chunk is bigger than remaining space"));
263 }
264
265 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
266 entryRemainingBytes_ -= contentSize;
267 if (entryRemainingBytes_ > 0) {
268 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
269 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
270 IIdentityCredentialStore::STATUS_INVALID_DATA,
271 "Retrieved non-final chunk which isn't kGcmChunkSize"));
272 }
273 }
274
275 optional<vector<uint8_t>> nonce = support::getRandom(12);
276 if (!nonce) {
277 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
278 IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
279 }
280 optional<vector<uint8_t>> encryptedContent =
281 support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
282 if (!encryptedContent) {
283 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
284 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
285 }
286
287 if (entryRemainingBytes_ == 0) {
288 // TODO: ideally do do this without parsing the data (but still validate data is valid
289 // CBOR).
290 auto [item, _, message] = cppbor::parse(entryBytes_);
291 if (item == nullptr) {
292 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
293 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
294 }
295 cppbor::Map entryMap;
296 entryMap.add("name", entryName_);
297 entryMap.add("value", std::move(item));
298 cppbor::Array profileIdArray;
299 for (auto id : entryAccessControlProfileIds_) {
300 profileIdArray.add(id);
301 }
302 entryMap.add("accessControlProfiles", std::move(profileIdArray));
303 signedDataCurrentNamespace_.add(std::move(entryMap));
304 }
305
Jooyung Han17be89b2020-02-21 21:17:06 +0900306 *outEncryptedContent = encryptedContent.value();
David Zeuthen81603152020-02-11 22:04:24 -0500307 return ndk::ScopedAStatus::ok();
308}
David Zeuthenc75ac312019-10-28 13:16:45 -0400309
310// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
311// |credentialPrivKey|.
312static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
313 const vector<uint8_t>& credentialPrivKey,
314 vector<uint8_t>& credentialKeys) {
315 if (storageKey.size() != 16) {
316 LOG(ERROR) << "Size of storageKey is not 16";
317 return false;
318 }
319
320 cppbor::Array array;
321 array.add(cppbor::Bstr(storageKey));
322 array.add(cppbor::Bstr(credentialPrivKey));
323 credentialKeys = array.encode();
324 return true;
325}
326
327// Writes CBOR-encoded structure to |credentialData| containing |docType|,
328// |testCredential| and |credentialKeys|. The latter element will be stored in
329// encrypted form, using |hardwareBoundKey| as the encryption key.
330bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
331 bool testCredential, const vector<uint8_t>& credentialKeys,
332 vector<uint8_t>& credentialData) {
333 optional<vector<uint8_t>> nonce = support::getRandom(12);
334 if (!nonce) {
335 LOG(ERROR) << "Error getting random";
336 return false;
337 }
338 vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
339 optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
340 hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
341 if (!credentialBlob) {
342 LOG(ERROR) << "Error encrypting CredentialKeys blob";
343 return false;
344 }
345
346 cppbor::Array array;
347 array.add(docType);
348 array.add(testCredential);
349 array.add(cppbor::Bstr(credentialBlob.value()));
350 credentialData = array.encode();
351 return true;
352}
353
David Zeuthen81603152020-02-11 22:04:24 -0500354ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
Jooyung Han17be89b2020-02-21 21:17:06 +0900355 vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
Selene Huang92b61d62020-03-04 02:24:16 -0800356 if (numAccessControlProfileRemaining_ != 0) {
357 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
358 IIdentityCredentialStore::STATUS_INVALID_DATA,
359 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
360 }
361
362 if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
363 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
364 IIdentityCredentialStore::STATUS_INVALID_DATA,
365 "More entry spaces remain than startPersonalization configured"));
366 }
367
David Zeuthenc75ac312019-10-28 13:16:45 -0400368 if (signedDataCurrentNamespace_.size() > 0) {
369 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
370 }
371 cppbor::Array popArray;
372 popArray.add("ProofOfProvisioning")
373 .add(docType_)
374 .add(std::move(signedDataAccessControlProfiles_))
375 .add(std::move(signedDataNamespaces_))
376 .add(testCredential_);
377 vector<uint8_t> encodedCbor = popArray.encode();
378
379 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
380 encodedCbor, // payload
381 {}, // additionalData
382 {}); // certificateChain
383 if (!signature) {
David Zeuthen81603152020-02-11 22:04:24 -0500384 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
385 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400386 }
387
388 vector<uint8_t> credentialKeys;
389 if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
David Zeuthen81603152020-02-11 22:04:24 -0500390 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
391 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400392 }
393
394 vector<uint8_t> credentialData;
David Zeuthen81603152020-02-11 22:04:24 -0500395 if (!generateCredentialData(
396 testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
397 docType_, testCredential_, credentialKeys, credentialData)) {
398 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
399 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400400 }
401
Jooyung Han17be89b2020-02-21 21:17:06 +0900402 *outCredentialData = credentialData;
403 *outProofOfProvisioningSignature = signature.value();
David Zeuthen81603152020-02-11 22:04:24 -0500404 return ndk::ScopedAStatus::ok();
David Zeuthenc75ac312019-10-28 13:16:45 -0400405}
406
David Zeuthen81603152020-02-11 22:04:24 -0500407} // namespace aidl::android::hardware::identity