blob: bce913aeec35bedda4114518d8582985e11f1d12 [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();
47
48 return true;
49}
50
Selene Huang459cb802020-01-08 22:59:02 -080051// This function generates the attestation certificate using the passed in
52// |attestationApplicationId| and |attestationChallenge|. It will generate an
53// attestation certificate with current time and expires one year from now. The
54// certificate shall contain all values as specified in hal.
David Zeuthen81603152020-02-11 22:04:24 -050055ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
Jooyung Han17be89b2020-02-21 21:17:06 +090056 const vector<uint8_t>& attestationApplicationId, //
57 const vector<uint8_t>& attestationChallenge, //
Selene Huang459cb802020-01-08 22:59:02 -080058 vector<Certificate>* outCertificateChain) {
59 if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
David Zeuthen81603152020-02-11 22:04:24 -050060 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
61 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080062 "Error attestation certificate previously generated"));
David Zeuthen81603152020-02-11 22:04:24 -050063 }
64
Selene Huang459cb802020-01-08 22:59:02 -080065 vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
66 vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
67
68 optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
69 support::createEcKeyPairAndAttestation(challenge, appId);
70 if (!keyAttestationPair) {
71 LOG(ERROR) << "Error creating credentialKey and attestation";
David Zeuthen81603152020-02-11 22:04:24 -050072 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
73 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080074 "Error creating credentialKey and attestation"));
David Zeuthen81603152020-02-11 22:04:24 -050075 }
76
Selene Huang459cb802020-01-08 22:59:02 -080077 vector<uint8_t> keyPair = keyAttestationPair.value().first;
78 certificateChain_ = keyAttestationPair.value().second;
David Zeuthen81603152020-02-11 22:04:24 -050079
Selene Huang459cb802020-01-08 22:59:02 -080080 optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
81 if (!pubKey) {
David Zeuthen81603152020-02-11 22:04:24 -050082 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
83 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080084 "Error getting public part of credentialKey"));
David Zeuthen81603152020-02-11 22:04:24 -050085 }
Selene Huang459cb802020-01-08 22:59:02 -080086 credentialPubKey_ = pubKey.value();
David Zeuthen81603152020-02-11 22:04:24 -050087
Selene Huang459cb802020-01-08 22:59:02 -080088 optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
89 if (!privKey) {
David Zeuthen81603152020-02-11 22:04:24 -050090 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
91 IIdentityCredentialStore::STATUS_FAILED,
Selene Huang459cb802020-01-08 22:59:02 -080092 "Error getting private part of credentialKey"));
David Zeuthen81603152020-02-11 22:04:24 -050093 }
Selene Huang459cb802020-01-08 22:59:02 -080094 credentialPrivKey_ = privKey.value();
David Zeuthen81603152020-02-11 22:04:24 -050095
Selene Huang459cb802020-01-08 22:59:02 -080096 // convert from vector<vector<uint8_t>>> to vector<Certificate>*
David Zeuthen81603152020-02-11 22:04:24 -050097 *outCertificateChain = vector<Certificate>();
Selene Huang459cb802020-01-08 22:59:02 -080098 for (const vector<uint8_t>& cert : certificateChain_) {
David Zeuthen81603152020-02-11 22:04:24 -050099 Certificate c = Certificate();
Jooyung Han17be89b2020-02-21 21:17:06 +0900100 c.encodedCertificate = cert;
David Zeuthen81603152020-02-11 22:04:24 -0500101 outCertificateChain->push_back(std::move(c));
102 }
103 return ndk::ScopedAStatus::ok();
104}
105
106ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
107 int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
108 numAccessControlProfileRemaining_ = accessControlProfileCount;
109 remainingEntryCounts_ = entryCounts;
110 entryNameSpace_ = "";
111
112 signedDataAccessControlProfiles_ = cppbor::Array();
113 signedDataNamespaces_ = cppbor::Map();
114 signedDataCurrentNamespace_ = cppbor::Array();
115
116 return ndk::ScopedAStatus::ok();
117}
118
119ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
120 int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
121 int64_t timeoutMillis, int64_t secureUserId,
122 SecureAccessControlProfile* outSecureAccessControlProfile) {
123 SecureAccessControlProfile profile;
124
125 if (numAccessControlProfileRemaining_ == 0) {
126 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
127 IIdentityCredentialStore::STATUS_INVALID_DATA,
128 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
129 }
130
131 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
132 // be zero.
133 if (!userAuthenticationRequired && timeoutMillis != 0) {
134 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
135 IIdentityCredentialStore::STATUS_INVALID_DATA,
136 "userAuthenticationRequired is false but timeout is non-zero"));
137 }
138
139 profile.id = id;
140 profile.readerCertificate = readerCertificate;
141 profile.userAuthenticationRequired = userAuthenticationRequired;
142 profile.timeoutMillis = timeoutMillis;
143 profile.secureUserId = secureUserId;
144 optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
145 if (!mac) {
146 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
147 IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
148 }
Jooyung Han17be89b2020-02-21 21:17:06 +0900149 profile.mac = mac.value();
David Zeuthen81603152020-02-11 22:04:24 -0500150
151 cppbor::Map profileMap;
152 profileMap.add("id", profile.id);
153 if (profile.readerCertificate.encodedCertificate.size() > 0) {
Jooyung Han17be89b2020-02-21 21:17:06 +0900154 profileMap.add("readerCertificate",
155 cppbor::Bstr(profile.readerCertificate.encodedCertificate));
David Zeuthen81603152020-02-11 22:04:24 -0500156 }
157 if (profile.userAuthenticationRequired) {
158 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
159 profileMap.add("timeoutMillis", profile.timeoutMillis);
160 }
161 signedDataAccessControlProfiles_.add(std::move(profileMap));
162
163 numAccessControlProfileRemaining_--;
164
165 *outSecureAccessControlProfile = profile;
166 return ndk::ScopedAStatus::ok();
167}
168
169ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
170 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
171 int32_t entrySize) {
172 if (numAccessControlProfileRemaining_ != 0) {
173 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
174 << " and expected zero";
175 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
176 IIdentityCredentialStore::STATUS_INVALID_DATA,
177 "numAccessControlProfileRemaining_ is not zero"));
178 }
179
180 if (remainingEntryCounts_.size() == 0) {
181 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
182 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
183 }
184
185 // Handle initial beginEntry() call.
186 if (entryNameSpace_ == "") {
187 entryNameSpace_ = nameSpace;
188 }
189
190 // If the namespace changed...
191 if (nameSpace != entryNameSpace_) {
192 // Then check that all entries in the previous namespace have been added..
193 if (remainingEntryCounts_[0] != 0) {
194 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
195 IIdentityCredentialStore::STATUS_INVALID_DATA,
196 "New namespace but a non-zero number of entries remain to be added"));
197 }
198 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
199
200 if (signedDataCurrentNamespace_.size() > 0) {
201 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
202 signedDataCurrentNamespace_ = cppbor::Array();
203 }
204 } else {
205 // Same namespace...
206 if (remainingEntryCounts_[0] == 0) {
207 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
208 IIdentityCredentialStore::STATUS_INVALID_DATA,
209 "Same namespace but no entries remain to be added"));
210 }
211 remainingEntryCounts_[0] -= 1;
212 }
213
214 entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
215
216 entryRemainingBytes_ = entrySize;
217 entryNameSpace_ = nameSpace;
218 entryName_ = name;
219 entryAccessControlProfileIds_ = accessControlProfileIds;
220 entryBytes_.resize(0);
221 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
222 return ndk::ScopedAStatus::ok();
223}
224
Jooyung Han17be89b2020-02-21 21:17:06 +0900225ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
226 vector<uint8_t>* outEncryptedContent) {
David Zeuthen81603152020-02-11 22:04:24 -0500227 size_t contentSize = content.size();
228
229 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
230 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
231 IIdentityCredentialStore::STATUS_INVALID_DATA,
232 "Passed in chunk of is bigger than kGcmChunkSize"));
233 }
234 if (contentSize > entryRemainingBytes_) {
235 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
236 IIdentityCredentialStore::STATUS_INVALID_DATA,
237 "Passed in chunk is bigger than remaining space"));
238 }
239
240 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
241 entryRemainingBytes_ -= contentSize;
242 if (entryRemainingBytes_ > 0) {
243 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
244 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
245 IIdentityCredentialStore::STATUS_INVALID_DATA,
246 "Retrieved non-final chunk which isn't kGcmChunkSize"));
247 }
248 }
249
250 optional<vector<uint8_t>> nonce = support::getRandom(12);
251 if (!nonce) {
252 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
253 IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
254 }
255 optional<vector<uint8_t>> encryptedContent =
256 support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
257 if (!encryptedContent) {
258 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
259 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
260 }
261
262 if (entryRemainingBytes_ == 0) {
263 // TODO: ideally do do this without parsing the data (but still validate data is valid
264 // CBOR).
265 auto [item, _, message] = cppbor::parse(entryBytes_);
266 if (item == nullptr) {
267 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
268 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
269 }
270 cppbor::Map entryMap;
271 entryMap.add("name", entryName_);
272 entryMap.add("value", std::move(item));
273 cppbor::Array profileIdArray;
274 for (auto id : entryAccessControlProfileIds_) {
275 profileIdArray.add(id);
276 }
277 entryMap.add("accessControlProfiles", std::move(profileIdArray));
278 signedDataCurrentNamespace_.add(std::move(entryMap));
279 }
280
Jooyung Han17be89b2020-02-21 21:17:06 +0900281 *outEncryptedContent = encryptedContent.value();
David Zeuthen81603152020-02-11 22:04:24 -0500282 return ndk::ScopedAStatus::ok();
283}
David Zeuthenc75ac312019-10-28 13:16:45 -0400284
285// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
286// |credentialPrivKey|.
287static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
288 const vector<uint8_t>& credentialPrivKey,
289 vector<uint8_t>& credentialKeys) {
290 if (storageKey.size() != 16) {
291 LOG(ERROR) << "Size of storageKey is not 16";
292 return false;
293 }
294
295 cppbor::Array array;
296 array.add(cppbor::Bstr(storageKey));
297 array.add(cppbor::Bstr(credentialPrivKey));
298 credentialKeys = array.encode();
299 return true;
300}
301
302// Writes CBOR-encoded structure to |credentialData| containing |docType|,
303// |testCredential| and |credentialKeys|. The latter element will be stored in
304// encrypted form, using |hardwareBoundKey| as the encryption key.
305bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
306 bool testCredential, const vector<uint8_t>& credentialKeys,
307 vector<uint8_t>& credentialData) {
308 optional<vector<uint8_t>> nonce = support::getRandom(12);
309 if (!nonce) {
310 LOG(ERROR) << "Error getting random";
311 return false;
312 }
313 vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
314 optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
315 hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
316 if (!credentialBlob) {
317 LOG(ERROR) << "Error encrypting CredentialKeys blob";
318 return false;
319 }
320
321 cppbor::Array array;
322 array.add(docType);
323 array.add(testCredential);
324 array.add(cppbor::Bstr(credentialBlob.value()));
325 credentialData = array.encode();
326 return true;
327}
328
David Zeuthen81603152020-02-11 22:04:24 -0500329ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
Jooyung Han17be89b2020-02-21 21:17:06 +0900330 vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
David Zeuthenc75ac312019-10-28 13:16:45 -0400331 if (signedDataCurrentNamespace_.size() > 0) {
332 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
333 }
334 cppbor::Array popArray;
335 popArray.add("ProofOfProvisioning")
336 .add(docType_)
337 .add(std::move(signedDataAccessControlProfiles_))
338 .add(std::move(signedDataNamespaces_))
339 .add(testCredential_);
340 vector<uint8_t> encodedCbor = popArray.encode();
341
342 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
343 encodedCbor, // payload
344 {}, // additionalData
345 {}); // certificateChain
346 if (!signature) {
David Zeuthen81603152020-02-11 22:04:24 -0500347 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
348 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400349 }
350
351 vector<uint8_t> credentialKeys;
352 if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
David Zeuthen81603152020-02-11 22:04:24 -0500353 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
354 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400355 }
356
357 vector<uint8_t> credentialData;
David Zeuthen81603152020-02-11 22:04:24 -0500358 if (!generateCredentialData(
359 testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
360 docType_, testCredential_, credentialKeys, credentialData)) {
361 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
362 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400363 }
364
Jooyung Han17be89b2020-02-21 21:17:06 +0900365 *outCredentialData = credentialData;
366 *outProofOfProvisioningSignature = signature.value();
David Zeuthen81603152020-02-11 22:04:24 -0500367 return ndk::ScopedAStatus::ok();
David Zeuthenc75ac312019-10-28 13:16:45 -0400368}
369
David Zeuthen81603152020-02-11 22:04:24 -0500370} // namespace aidl::android::hardware::identity