blob: 1328f3629eb242cedaa3d682e778d558a33e3e16 [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"
Selene Huang459cb802020-01-08 22:59:02 -080020
David Zeuthenc75ac312019-10-28 13:16:45 -040021#include <android/hardware/identity/support/IdentityCredentialSupport.h>
22
23#include <android-base/logging.h>
David Zeuthen28edb102020-04-28 18:54:55 -040024#include <android-base/stringprintf.h>
David Zeuthenc75ac312019-10-28 13:16:45 -040025
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"
David Zeuthen630de2a2020-05-11 14:04:54 -040032
33#include "FakeSecureHardwareProxy.h"
David Zeuthen81603152020-02-11 22:04:24 -050034
35namespace aidl::android::hardware::identity {
David Zeuthenc75ac312019-10-28 13:16:45 -040036
David Zeuthen28edb102020-04-28 18:54:55 -040037using ::android::base::StringPrintf;
David Zeuthenc75ac312019-10-28 13:16:45 -040038using ::std::optional;
David Zeuthen81603152020-02-11 22:04:24 -050039using namespace ::android::hardware::identity;
40
41bool WritableIdentityCredential::initialize() {
David Zeuthen630de2a2020-05-11 14:04:54 -040042 if (!hwProxy_->initialize(testCredential_)) {
43 LOG(ERROR) << "hwProxy->initialize failed";
David Zeuthen81603152020-02-11 22:04:24 -050044 return false;
45 }
Selene Huang92b61d62020-03-04 02:24:16 -080046 startPersonalizationCalled_ = false;
47 firstEntry_ = true;
David Zeuthen81603152020-02-11 22:04:24 -050048
49 return true;
50}
51
David Zeuthen630de2a2020-05-11 14:04:54 -040052WritableIdentityCredential::~WritableIdentityCredential() {}
53
David Zeuthen81603152020-02-11 22:04:24 -050054ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
David Zeuthen630de2a2020-05-11 14:04:54 -040055 const vector<uint8_t>& attestationApplicationId,
56 const vector<uint8_t>& attestationChallenge, vector<Certificate>* outCertificateChain) {
57 if (getAttestationCertificateAlreadyCalled_) {
David Zeuthen81603152020-02-11 22:04:24 -050058 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
59 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080060 "Error attestation certificate previously generated"));
David Zeuthen81603152020-02-11 22:04:24 -050061 }
David Zeuthen630de2a2020-05-11 14:04:54 -040062 getAttestationCertificateAlreadyCalled_ = true;
63
David Zeuthenef739512020-06-03 13:24:52 -040064 if (attestationChallenge.empty()) {
65 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
66 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
67 }
David Zeuthen81603152020-02-11 22:04:24 -050068
David Zeuthen630de2a2020-05-11 14:04:54 -040069 optional<vector<uint8_t>> certChain =
70 hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
71 if (!certChain) {
David Zeuthen81603152020-02-11 22:04:24 -050072 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
73 IIdentityCredentialStore::STATUS_FAILED,
David Zeuthen630de2a2020-05-11 14:04:54 -040074 "Error generating attestation certificate chain"));
David Zeuthen81603152020-02-11 22:04:24 -050075 }
76
David Zeuthen630de2a2020-05-11 14:04:54 -040077 optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
78 if (!certs) {
David Zeuthen81603152020-02-11 22:04:24 -050079 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
80 IIdentityCredentialStore::STATUS_FAILED,
David Zeuthen630de2a2020-05-11 14:04:54 -040081 "Error splitting chain into separate certificates"));
David Zeuthen81603152020-02-11 22:04:24 -050082 }
83
David Zeuthen81603152020-02-11 22:04:24 -050084 *outCertificateChain = vector<Certificate>();
David Zeuthen630de2a2020-05-11 14:04:54 -040085 for (const vector<uint8_t>& cert : certs.value()) {
David Zeuthen81603152020-02-11 22:04:24 -050086 Certificate c = Certificate();
Jooyung Han17be89b2020-02-21 21:17:06 +090087 c.encodedCertificate = cert;
David Zeuthen81603152020-02-11 22:04:24 -050088 outCertificateChain->push_back(std::move(c));
89 }
David Zeuthen630de2a2020-05-11 14:04:54 -040090
David Zeuthen81603152020-02-11 22:04:24 -050091 return ndk::ScopedAStatus::ok();
92}
93
David Zeuthen28edb102020-04-28 18:54:55 -040094ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
95 int32_t expectedProofOfProvisioningSize) {
96 expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
97 return ndk::ScopedAStatus::ok();
98}
99
David Zeuthen81603152020-02-11 22:04:24 -0500100ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
101 int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
Selene Huang92b61d62020-03-04 02:24:16 -0800102 if (startPersonalizationCalled_) {
103 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
104 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
105 }
Selene Huang92b61d62020-03-04 02:24:16 -0800106 startPersonalizationCalled_ = true;
David Zeuthen630de2a2020-05-11 14:04:54 -0400107
David Zeuthen81603152020-02-11 22:04:24 -0500108 numAccessControlProfileRemaining_ = accessControlProfileCount;
109 remainingEntryCounts_ = entryCounts;
110 entryNameSpace_ = "";
111
112 signedDataAccessControlProfiles_ = cppbor::Array();
113 signedDataNamespaces_ = cppbor::Map();
114 signedDataCurrentNamespace_ = cppbor::Array();
115
David Zeuthen630de2a2020-05-11 14:04:54 -0400116 if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
117 expectedProofOfProvisioningSize_)) {
118 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
119 IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
120 }
121
David Zeuthen81603152020-02-11 22:04:24 -0500122 return ndk::ScopedAStatus::ok();
123}
124
125ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
126 int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
127 int64_t timeoutMillis, int64_t secureUserId,
128 SecureAccessControlProfile* outSecureAccessControlProfile) {
David Zeuthen81603152020-02-11 22:04:24 -0500129 if (numAccessControlProfileRemaining_ == 0) {
130 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
131 IIdentityCredentialStore::STATUS_INVALID_DATA,
132 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
133 }
134
Selene Huang92b61d62020-03-04 02:24:16 -0800135 if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
136 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
137 IIdentityCredentialStore::STATUS_INVALID_DATA,
138 "Access Control Profile id must be unique"));
139 }
140 accessControlProfileIds_.insert(id);
141
David Zeuthena0796e92020-04-27 15:24:55 -0400142 if (id < 0 || id >= 32) {
143 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
144 IIdentityCredentialStore::STATUS_INVALID_DATA,
145 "Access Control Profile id must be non-negative and less than 32"));
146 }
147
David Zeuthen81603152020-02-11 22:04:24 -0500148 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
149 // be zero.
150 if (!userAuthenticationRequired && timeoutMillis != 0) {
151 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
152 IIdentityCredentialStore::STATUS_INVALID_DATA,
153 "userAuthenticationRequired is false but timeout is non-zero"));
154 }
155
David Zeuthen630de2a2020-05-11 14:04:54 -0400156 optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
157 id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
158 secureUserId);
159 if (!mac) {
David Zeuthenef739512020-06-03 13:24:52 -0400160 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
David Zeuthen630de2a2020-05-11 14:04:54 -0400161 IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
David Zeuthenef739512020-06-03 13:24:52 -0400162 }
163
David Zeuthen630de2a2020-05-11 14:04:54 -0400164 SecureAccessControlProfile profile;
David Zeuthen81603152020-02-11 22:04:24 -0500165 profile.id = id;
166 profile.readerCertificate = readerCertificate;
167 profile.userAuthenticationRequired = userAuthenticationRequired;
168 profile.timeoutMillis = timeoutMillis;
169 profile.secureUserId = secureUserId;
Jooyung Han17be89b2020-02-21 21:17:06 +0900170 profile.mac = mac.value();
David Zeuthen81603152020-02-11 22:04:24 -0500171 cppbor::Map profileMap;
172 profileMap.add("id", profile.id);
173 if (profile.readerCertificate.encodedCertificate.size() > 0) {
Jooyung Han17be89b2020-02-21 21:17:06 +0900174 profileMap.add("readerCertificate",
175 cppbor::Bstr(profile.readerCertificate.encodedCertificate));
David Zeuthen81603152020-02-11 22:04:24 -0500176 }
177 if (profile.userAuthenticationRequired) {
178 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
179 profileMap.add("timeoutMillis", profile.timeoutMillis);
180 }
181 signedDataAccessControlProfiles_.add(std::move(profileMap));
182
183 numAccessControlProfileRemaining_--;
184
185 *outSecureAccessControlProfile = profile;
186 return ndk::ScopedAStatus::ok();
187}
188
189ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
190 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
191 int32_t entrySize) {
192 if (numAccessControlProfileRemaining_ != 0) {
193 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
194 << " and expected zero";
195 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
196 IIdentityCredentialStore::STATUS_INVALID_DATA,
197 "numAccessControlProfileRemaining_ is not zero"));
198 }
199
200 if (remainingEntryCounts_.size() == 0) {
201 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
202 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
203 }
204
205 // Handle initial beginEntry() call.
Selene Huang92b61d62020-03-04 02:24:16 -0800206 if (firstEntry_) {
207 firstEntry_ = false;
David Zeuthen81603152020-02-11 22:04:24 -0500208 entryNameSpace_ = nameSpace;
Selene Huang92b61d62020-03-04 02:24:16 -0800209 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500210 }
211
212 // If the namespace changed...
213 if (nameSpace != entryNameSpace_) {
Selene Huang92b61d62020-03-04 02:24:16 -0800214 if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
215 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
216 IIdentityCredentialStore::STATUS_INVALID_DATA,
217 "Name space cannot be added in interleaving fashion"));
218 }
219
David Zeuthen81603152020-02-11 22:04:24 -0500220 // Then check that all entries in the previous namespace have been added..
221 if (remainingEntryCounts_[0] != 0) {
222 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
223 IIdentityCredentialStore::STATUS_INVALID_DATA,
224 "New namespace but a non-zero number of entries remain to be added"));
225 }
226 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
Selene Huang92b61d62020-03-04 02:24:16 -0800227 remainingEntryCounts_[0] -= 1;
228 allNameSpaces_.insert(nameSpace);
David Zeuthen81603152020-02-11 22:04:24 -0500229
230 if (signedDataCurrentNamespace_.size() > 0) {
231 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
232 signedDataCurrentNamespace_ = cppbor::Array();
233 }
234 } else {
235 // Same namespace...
236 if (remainingEntryCounts_[0] == 0) {
237 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
238 IIdentityCredentialStore::STATUS_INVALID_DATA,
239 "Same namespace but no entries remain to be added"));
240 }
241 remainingEntryCounts_[0] -= 1;
242 }
243
David Zeuthen81603152020-02-11 22:04:24 -0500244 entryRemainingBytes_ = entrySize;
245 entryNameSpace_ = nameSpace;
246 entryName_ = name;
247 entryAccessControlProfileIds_ = accessControlProfileIds;
248 entryBytes_.resize(0);
249 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
David Zeuthen630de2a2020-05-11 14:04:54 -0400250
251 if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
252 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
253 IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
254 }
255
David Zeuthen81603152020-02-11 22:04:24 -0500256 return ndk::ScopedAStatus::ok();
257}
258
Jooyung Han17be89b2020-02-21 21:17:06 +0900259ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
260 vector<uint8_t>* outEncryptedContent) {
David Zeuthen81603152020-02-11 22:04:24 -0500261 size_t contentSize = content.size();
262
263 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
264 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
265 IIdentityCredentialStore::STATUS_INVALID_DATA,
266 "Passed in chunk of is bigger than kGcmChunkSize"));
267 }
268 if (contentSize > entryRemainingBytes_) {
269 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
270 IIdentityCredentialStore::STATUS_INVALID_DATA,
271 "Passed in chunk is bigger than remaining space"));
272 }
273
274 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
275 entryRemainingBytes_ -= contentSize;
276 if (entryRemainingBytes_ > 0) {
277 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
278 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
279 IIdentityCredentialStore::STATUS_INVALID_DATA,
280 "Retrieved non-final chunk which isn't kGcmChunkSize"));
281 }
282 }
283
David Zeuthen630de2a2020-05-11 14:04:54 -0400284 optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
285 entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
David Zeuthen81603152020-02-11 22:04:24 -0500286 if (!encryptedContent) {
287 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
David Zeuthen630de2a2020-05-11 14:04:54 -0400288 IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
David Zeuthen81603152020-02-11 22:04:24 -0500289 }
290
291 if (entryRemainingBytes_ == 0) {
292 // TODO: ideally do do this without parsing the data (but still validate data is valid
293 // CBOR).
294 auto [item, _, message] = cppbor::parse(entryBytes_);
295 if (item == nullptr) {
296 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
297 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
298 }
299 cppbor::Map entryMap;
300 entryMap.add("name", entryName_);
301 entryMap.add("value", std::move(item));
302 cppbor::Array profileIdArray;
303 for (auto id : entryAccessControlProfileIds_) {
304 profileIdArray.add(id);
305 }
306 entryMap.add("accessControlProfiles", std::move(profileIdArray));
307 signedDataCurrentNamespace_.add(std::move(entryMap));
308 }
309
Jooyung Han17be89b2020-02-21 21:17:06 +0900310 *outEncryptedContent = encryptedContent.value();
David Zeuthen81603152020-02-11 22:04:24 -0500311 return ndk::ScopedAStatus::ok();
312}
David Zeuthenc75ac312019-10-28 13:16:45 -0400313
David Zeuthen81603152020-02-11 22:04:24 -0500314ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
Jooyung Han17be89b2020-02-21 21:17:06 +0900315 vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
Selene Huang92b61d62020-03-04 02:24:16 -0800316 if (numAccessControlProfileRemaining_ != 0) {
317 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
318 IIdentityCredentialStore::STATUS_INVALID_DATA,
319 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
320 }
321
322 if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
323 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
324 IIdentityCredentialStore::STATUS_INVALID_DATA,
325 "More entry spaces remain than startPersonalization configured"));
326 }
327
David Zeuthenc75ac312019-10-28 13:16:45 -0400328 if (signedDataCurrentNamespace_.size() > 0) {
329 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
330 }
331 cppbor::Array popArray;
332 popArray.add("ProofOfProvisioning")
333 .add(docType_)
334 .add(std::move(signedDataAccessControlProfiles_))
335 .add(std::move(signedDataNamespaces_))
336 .add(testCredential_);
337 vector<uint8_t> encodedCbor = popArray.encode();
338
David Zeuthen28edb102020-04-28 18:54:55 -0400339 if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
340 LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
341 << "was expecting " << expectedProofOfProvisioningSize_;
342 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
343 IIdentityCredentialStore::STATUS_INVALID_DATA,
344 StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
345 encodedCbor.size(), expectedProofOfProvisioningSize_)
346 .c_str()));
347 }
348
David Zeuthen630de2a2020-05-11 14:04:54 -0400349 optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
350 if (!signatureOfToBeSigned) {
351 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
352 IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
353 }
354
355 optional<vector<uint8_t>> signature =
356 support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
357 encodedCbor, // data
358 {}); // certificateChain
David Zeuthenc75ac312019-10-28 13:16:45 -0400359 if (!signature) {
David Zeuthen81603152020-02-11 22:04:24 -0500360 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
361 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400362 }
363
David Zeuthen630de2a2020-05-11 14:04:54 -0400364 optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
365 if (!encryptedCredentialKeys) {
David Zeuthen81603152020-02-11 22:04:24 -0500366 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
David Zeuthen630de2a2020-05-11 14:04:54 -0400367 IIdentityCredentialStore::STATUS_FAILED,
368 "Error generating encrypted CredentialKeys"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400369 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400370 cppbor::Array array;
371 array.add(docType_);
372 array.add(testCredential_);
373 array.add(encryptedCredentialKeys.value());
374 vector<uint8_t> credentialData = array.encode();
David Zeuthenc75ac312019-10-28 13:16:45 -0400375
Jooyung Han17be89b2020-02-21 21:17:06 +0900376 *outCredentialData = credentialData;
377 *outProofOfProvisioningSignature = signature.value();
David Zeuthen630de2a2020-05-11 14:04:54 -0400378 hwProxy_->shutdown();
379
David Zeuthen81603152020-02-11 22:04:24 -0500380 return ndk::ScopedAStatus::ok();
David Zeuthenc75ac312019-10-28 13:16:45 -0400381}
382
David Zeuthen81603152020-02-11 22:04:24 -0500383} // namespace aidl::android::hardware::identity