blob: 89f7f356967dbd81211c2ad8c7ac557c48c29bce [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(
Selene Huang459cb802020-01-08 22:59:02 -080056 const vector<int8_t>& attestationApplicationId, //
57 const vector<int8_t>& attestationChallenge, //
58 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();
100 c.encodedCertificate = byteStringToSigned(cert);
101 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 }
149 profile.mac = byteStringToSigned(mac.value());
150
151 cppbor::Map profileMap;
152 profileMap.add("id", profile.id);
153 if (profile.readerCertificate.encodedCertificate.size() > 0) {
154 profileMap.add(
155 "readerCertificate",
156 cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
157 }
158 if (profile.userAuthenticationRequired) {
159 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
160 profileMap.add("timeoutMillis", profile.timeoutMillis);
161 }
162 signedDataAccessControlProfiles_.add(std::move(profileMap));
163
164 numAccessControlProfileRemaining_--;
165
166 *outSecureAccessControlProfile = profile;
167 return ndk::ScopedAStatus::ok();
168}
169
170ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
171 const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
172 int32_t entrySize) {
173 if (numAccessControlProfileRemaining_ != 0) {
174 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
175 << " and expected zero";
176 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
177 IIdentityCredentialStore::STATUS_INVALID_DATA,
178 "numAccessControlProfileRemaining_ is not zero"));
179 }
180
181 if (remainingEntryCounts_.size() == 0) {
182 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
183 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
184 }
185
186 // Handle initial beginEntry() call.
187 if (entryNameSpace_ == "") {
188 entryNameSpace_ = nameSpace;
189 }
190
191 // If the namespace changed...
192 if (nameSpace != entryNameSpace_) {
193 // Then check that all entries in the previous namespace have been added..
194 if (remainingEntryCounts_[0] != 0) {
195 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
196 IIdentityCredentialStore::STATUS_INVALID_DATA,
197 "New namespace but a non-zero number of entries remain to be added"));
198 }
199 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
200
201 if (signedDataCurrentNamespace_.size() > 0) {
202 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
203 signedDataCurrentNamespace_ = cppbor::Array();
204 }
205 } else {
206 // Same namespace...
207 if (remainingEntryCounts_[0] == 0) {
208 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
209 IIdentityCredentialStore::STATUS_INVALID_DATA,
210 "Same namespace but no entries remain to be added"));
211 }
212 remainingEntryCounts_[0] -= 1;
213 }
214
215 entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
216
217 entryRemainingBytes_ = entrySize;
218 entryNameSpace_ = nameSpace;
219 entryName_ = name;
220 entryAccessControlProfileIds_ = accessControlProfileIds;
221 entryBytes_.resize(0);
222 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
223 return ndk::ScopedAStatus::ok();
224}
225
226ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
227 vector<int8_t>* outEncryptedContent) {
228 auto content = byteStringToUnsigned(contentS);
229 size_t contentSize = content.size();
230
231 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
232 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
233 IIdentityCredentialStore::STATUS_INVALID_DATA,
234 "Passed in chunk of is bigger than kGcmChunkSize"));
235 }
236 if (contentSize > entryRemainingBytes_) {
237 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
238 IIdentityCredentialStore::STATUS_INVALID_DATA,
239 "Passed in chunk is bigger than remaining space"));
240 }
241
242 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
243 entryRemainingBytes_ -= contentSize;
244 if (entryRemainingBytes_ > 0) {
245 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
246 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
247 IIdentityCredentialStore::STATUS_INVALID_DATA,
248 "Retrieved non-final chunk which isn't kGcmChunkSize"));
249 }
250 }
251
252 optional<vector<uint8_t>> nonce = support::getRandom(12);
253 if (!nonce) {
254 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
255 IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
256 }
257 optional<vector<uint8_t>> encryptedContent =
258 support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
259 if (!encryptedContent) {
260 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
261 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
262 }
263
264 if (entryRemainingBytes_ == 0) {
265 // TODO: ideally do do this without parsing the data (but still validate data is valid
266 // CBOR).
267 auto [item, _, message] = cppbor::parse(entryBytes_);
268 if (item == nullptr) {
269 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
270 IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
271 }
272 cppbor::Map entryMap;
273 entryMap.add("name", entryName_);
274 entryMap.add("value", std::move(item));
275 cppbor::Array profileIdArray;
276 for (auto id : entryAccessControlProfileIds_) {
277 profileIdArray.add(id);
278 }
279 entryMap.add("accessControlProfiles", std::move(profileIdArray));
280 signedDataCurrentNamespace_.add(std::move(entryMap));
281 }
282
283 *outEncryptedContent = byteStringToSigned(encryptedContent.value());
284 return ndk::ScopedAStatus::ok();
285}
David Zeuthenc75ac312019-10-28 13:16:45 -0400286
287// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
288// |credentialPrivKey|.
289static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
290 const vector<uint8_t>& credentialPrivKey,
291 vector<uint8_t>& credentialKeys) {
292 if (storageKey.size() != 16) {
293 LOG(ERROR) << "Size of storageKey is not 16";
294 return false;
295 }
296
297 cppbor::Array array;
298 array.add(cppbor::Bstr(storageKey));
299 array.add(cppbor::Bstr(credentialPrivKey));
300 credentialKeys = array.encode();
301 return true;
302}
303
304// Writes CBOR-encoded structure to |credentialData| containing |docType|,
305// |testCredential| and |credentialKeys|. The latter element will be stored in
306// encrypted form, using |hardwareBoundKey| as the encryption key.
307bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
308 bool testCredential, const vector<uint8_t>& credentialKeys,
309 vector<uint8_t>& credentialData) {
310 optional<vector<uint8_t>> nonce = support::getRandom(12);
311 if (!nonce) {
312 LOG(ERROR) << "Error getting random";
313 return false;
314 }
315 vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
316 optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
317 hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
318 if (!credentialBlob) {
319 LOG(ERROR) << "Error encrypting CredentialKeys blob";
320 return false;
321 }
322
323 cppbor::Array array;
324 array.add(docType);
325 array.add(testCredential);
326 array.add(cppbor::Bstr(credentialBlob.value()));
327 credentialData = array.encode();
328 return true;
329}
330
David Zeuthen81603152020-02-11 22:04:24 -0500331ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
332 vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
David Zeuthenc75ac312019-10-28 13:16:45 -0400333 if (signedDataCurrentNamespace_.size() > 0) {
334 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
335 }
336 cppbor::Array popArray;
337 popArray.add("ProofOfProvisioning")
338 .add(docType_)
339 .add(std::move(signedDataAccessControlProfiles_))
340 .add(std::move(signedDataNamespaces_))
341 .add(testCredential_);
342 vector<uint8_t> encodedCbor = popArray.encode();
343
344 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
345 encodedCbor, // payload
346 {}, // additionalData
347 {}); // certificateChain
348 if (!signature) {
David Zeuthen81603152020-02-11 22:04:24 -0500349 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
350 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400351 }
352
353 vector<uint8_t> credentialKeys;
354 if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
David Zeuthen81603152020-02-11 22:04:24 -0500355 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
356 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400357 }
358
359 vector<uint8_t> credentialData;
David Zeuthen81603152020-02-11 22:04:24 -0500360 if (!generateCredentialData(
361 testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
362 docType_, testCredential_, credentialKeys, credentialData)) {
363 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
364 IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
David Zeuthenc75ac312019-10-28 13:16:45 -0400365 }
366
David Zeuthen81603152020-02-11 22:04:24 -0500367 *outCredentialData = byteStringToSigned(credentialData);
368 *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
369 return ndk::ScopedAStatus::ok();
David Zeuthenc75ac312019-10-28 13:16:45 -0400370}
371
David Zeuthen81603152020-02-11 22:04:24 -0500372} // namespace aidl::android::hardware::identity