blob: b3924447b2cf88b177b3ba0cd059bd28a7f62e20 [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 Zeuthena0796e92020-04-27 15:24:55 -0400146 if (id < 0 || id >= 32) {
147 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
148 IIdentityCredentialStore::STATUS_INVALID_DATA,
149 "Access Control Profile id must be non-negative and less than 32"));
150 }
151
David Zeuthen81603152020-02-11 22:04:24 -0500152 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
153 // be zero.
154 if (!userAuthenticationRequired && timeoutMillis != 0) {
155 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
156 IIdentityCredentialStore::STATUS_INVALID_DATA,
157 "userAuthenticationRequired is false but timeout is non-zero"));
158 }
159
160 profile.id = id;
161 profile.readerCertificate = readerCertificate;
162 profile.userAuthenticationRequired = userAuthenticationRequired;
163 profile.timeoutMillis = timeoutMillis;
164 profile.secureUserId = secureUserId;
165 optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
166 if (!mac) {
167 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
168 IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
169 }
Jooyung Han17be89b2020-02-21 21:17:06 +0900170 profile.mac = mac.value();
David Zeuthen81603152020-02-11 22:04:24 -0500171
172 cppbor::Map profileMap;
173 profileMap.add("id", profile.id);
174 if (profile.readerCertificate.encodedCertificate.size() > 0) {
Jooyung Han17be89b2020-02-21 21:17:06 +0900175 profileMap.add("readerCertificate",
176 cppbor::Bstr(profile.readerCertificate.encodedCertificate));
David Zeuthen81603152020-02-11 22:04:24 -0500177 }
178 if (profile.userAuthenticationRequired) {
179 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
180 profileMap.add("timeoutMillis", profile.timeoutMillis);
181 }
182 signedDataAccessControlProfiles_.add(std::move(profileMap));
183
184 numAccessControlProfileRemaining_--;
185
186 *outSecureAccessControlProfile = profile;
187 return ndk::ScopedAStatus::ok();
188}
189
190ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
191 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
192 int32_t entrySize) {
193 if (numAccessControlProfileRemaining_ != 0) {
194 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
195 << " and expected zero";
196 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
197 IIdentityCredentialStore::STATUS_INVALID_DATA,
198 "numAccessControlProfileRemaining_ is not zero"));
199 }
200
201 if (remainingEntryCounts_.size() == 0) {
202 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
203 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
204 }
205
206 // Handle initial beginEntry() call.
Selene Huang92b61d62020-03-04 02:24:16 -0800207 if (firstEntry_) {
208 firstEntry_ = false;
David Zeuthen81603152020-02-11 22:04:24 -0500209 entryNameSpace_ = nameSpace;
Selene Huang92b61d62020-03-04 02:24:16 -0800210 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500211 }
212
213 // If the namespace changed...
214 if (nameSpace != entryNameSpace_) {
Selene Huang92b61d62020-03-04 02:24:16 -0800215 if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
216 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
217 IIdentityCredentialStore::STATUS_INVALID_DATA,
218 "Name space cannot be added in interleaving fashion"));
219 }
220
David Zeuthen81603152020-02-11 22:04:24 -0500221 // Then check that all entries in the previous namespace have been added..
222 if (remainingEntryCounts_[0] != 0) {
223 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
224 IIdentityCredentialStore::STATUS_INVALID_DATA,
225 "New namespace but a non-zero number of entries remain to be added"));
226 }
227 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
Selene Huang92b61d62020-03-04 02:24:16 -0800228 remainingEntryCounts_[0] -= 1;
229 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500230
231 if (signedDataCurrentNamespace_.size() > 0) {
232 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
233 signedDataCurrentNamespace_ = cppbor::Array();
234 }
235 } else {
236 // Same namespace...
237 if (remainingEntryCounts_[0] == 0) {
238 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
239 IIdentityCredentialStore::STATUS_INVALID_DATA,
240 "Same namespace but no entries remain to be added"));
241 }
242 remainingEntryCounts_[0] -= 1;
243 }
244
245 entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
246
247 entryRemainingBytes_ = entrySize;
248 entryNameSpace_ = nameSpace;
249 entryName_ = name;
250 entryAccessControlProfileIds_ = accessControlProfileIds;
251 entryBytes_.resize(0);
252 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
253 return ndk::ScopedAStatus::ok();
254}
255
Jooyung Han17be89b2020-02-21 21:17:06 +0900256ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
257 vector<uint8_t>* outEncryptedContent) {
David Zeuthen81603152020-02-11 22:04:24 -0500258 size_t contentSize = content.size();
259
260 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
261 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
262 IIdentityCredentialStore::STATUS_INVALID_DATA,
263 "Passed in chunk of is bigger than kGcmChunkSize"));
264 }
265 if (contentSize > entryRemainingBytes_) {
266 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
267 IIdentityCredentialStore::STATUS_INVALID_DATA,
268 "Passed in chunk is bigger than remaining space"));
269 }
270
271 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
272 entryRemainingBytes_ -= contentSize;
273 if (entryRemainingBytes_ > 0) {
274 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
275 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
276 IIdentityCredentialStore::STATUS_INVALID_DATA,
277 "Retrieved non-final chunk which isn't kGcmChunkSize"));
278 }
279 }
280
281 optional<vector<uint8_t>> nonce = support::getRandom(12);
282 if (!nonce) {
283 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
284 IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
285 }
286 optional<vector<uint8_t>> encryptedContent =
287 support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
288 if (!encryptedContent) {
289 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
290 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
291 }
292
293 if (entryRemainingBytes_ == 0) {
294 // TODO: ideally do do this without parsing the data (but still validate data is valid
295 // CBOR).
296 auto [item, _, message] = cppbor::parse(entryBytes_);
297 if (item == nullptr) {
298 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
299 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
300 }
301 cppbor::Map entryMap;
302 entryMap.add("name", entryName_);
303 entryMap.add("value", std::move(item));
304 cppbor::Array profileIdArray;
305 for (auto id : entryAccessControlProfileIds_) {
306 profileIdArray.add(id);
307 }
308 entryMap.add("accessControlProfiles", std::move(profileIdArray));
309 signedDataCurrentNamespace_.add(std::move(entryMap));
310 }
311
Jooyung Han17be89b2020-02-21 21:17:06 +0900312 *outEncryptedContent = encryptedContent.value();
David Zeuthen81603152020-02-11 22:04:24 -0500313 return ndk::ScopedAStatus::ok();
314}
David Zeuthenc75ac312019-10-28 13:16:45 -0400315
316// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
317// |credentialPrivKey|.
318static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
319 const vector<uint8_t>& credentialPrivKey,
320 vector<uint8_t>& credentialKeys) {
321 if (storageKey.size() != 16) {
322 LOG(ERROR) << "Size of storageKey is not 16";
323 return false;
324 }
325
326 cppbor::Array array;
327 array.add(cppbor::Bstr(storageKey));
328 array.add(cppbor::Bstr(credentialPrivKey));
329 credentialKeys = array.encode();
330 return true;
331}
332
333// Writes CBOR-encoded structure to |credentialData| containing |docType|,
334// |testCredential| and |credentialKeys|. The latter element will be stored in
335// encrypted form, using |hardwareBoundKey| as the encryption key.
336bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
337 bool testCredential, const vector<uint8_t>& credentialKeys,
338 vector<uint8_t>& credentialData) {
339 optional<vector<uint8_t>> nonce = support::getRandom(12);
340 if (!nonce) {
341 LOG(ERROR) << "Error getting random";
342 return false;
343 }
344 vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
345 optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
346 hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
347 if (!credentialBlob) {
348 LOG(ERROR) << "Error encrypting CredentialKeys blob";
349 return false;
350 }
351
352 cppbor::Array array;
353 array.add(docType);
354 array.add(testCredential);
355 array.add(cppbor::Bstr(credentialBlob.value()));
356 credentialData = array.encode();
357 return true;
358}
359
David Zeuthen81603152020-02-11 22:04:24 -0500360ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
Jooyung Han17be89b2020-02-21 21:17:06 +0900361 vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
Selene Huang92b61d62020-03-04 02:24:16 -0800362 if (numAccessControlProfileRemaining_ != 0) {
363 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
364 IIdentityCredentialStore::STATUS_INVALID_DATA,
365 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
366 }
367
368 if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
369 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
370 IIdentityCredentialStore::STATUS_INVALID_DATA,
371 "More entry spaces remain than startPersonalization configured"));
372 }
373
David Zeuthenc75ac312019-10-28 13:16:45 -0400374 if (signedDataCurrentNamespace_.size() > 0) {
375 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
376 }
377 cppbor::Array popArray;
378 popArray.add("ProofOfProvisioning")
379 .add(docType_)
380 .add(std::move(signedDataAccessControlProfiles_))
381 .add(std::move(signedDataNamespaces_))
382 .add(testCredential_);
383 vector<uint8_t> encodedCbor = popArray.encode();
384
385 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
386 encodedCbor, // payload
387 {}, // additionalData
388 {}); // certificateChain
389 if (!signature) {
David Zeuthen81603152020-02-11 22:04:24 -0500390 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
391 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400392 }
393
394 vector<uint8_t> credentialKeys;
395 if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
David Zeuthen81603152020-02-11 22:04:24 -0500396 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
397 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400398 }
399
400 vector<uint8_t> credentialData;
David Zeuthen81603152020-02-11 22:04:24 -0500401 if (!generateCredentialData(
402 testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
403 docType_, testCredential_, credentialKeys, credentialData)) {
404 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
405 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400406 }
407
Jooyung Han17be89b2020-02-21 21:17:06 +0900408 *outCredentialData = credentialData;
409 *outProofOfProvisioningSignature = signature.value();
David Zeuthen81603152020-02-11 22:04:24 -0500410 return ndk::ScopedAStatus::ok();
David Zeuthenc75ac312019-10-28 13:16:45 -0400411}
412
David Zeuthen81603152020-02-11 22:04:24 -0500413} // namespace aidl::android::hardware::identity