blob: 548b4c071a3e7dcae7c90bd9e97850fadfdc9b66 [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
19#include "WritableIdentityCredential.h"
20#include "IdentityCredentialStore.h"
21
22#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
29namespace android {
30namespace hardware {
31namespace identity {
32namespace implementation {
33
34using ::std::optional;
35
36// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
37// |credentialPrivKey|.
38static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
39 const vector<uint8_t>& credentialPrivKey,
40 vector<uint8_t>& credentialKeys) {
41 if (storageKey.size() != 16) {
42 LOG(ERROR) << "Size of storageKey is not 16";
43 return false;
44 }
45
46 cppbor::Array array;
47 array.add(cppbor::Bstr(storageKey));
48 array.add(cppbor::Bstr(credentialPrivKey));
49 credentialKeys = array.encode();
50 return true;
51}
52
53// Writes CBOR-encoded structure to |credentialData| containing |docType|,
54// |testCredential| and |credentialKeys|. The latter element will be stored in
55// encrypted form, using |hardwareBoundKey| as the encryption key.
56bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
57 bool testCredential, const vector<uint8_t>& credentialKeys,
58 vector<uint8_t>& credentialData) {
59 optional<vector<uint8_t>> nonce = support::getRandom(12);
60 if (!nonce) {
61 LOG(ERROR) << "Error getting random";
62 return false;
63 }
64 vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
65 optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
66 hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
67 if (!credentialBlob) {
68 LOG(ERROR) << "Error encrypting CredentialKeys blob";
69 return false;
70 }
71
72 cppbor::Array array;
73 array.add(docType);
74 array.add(testCredential);
75 array.add(cppbor::Bstr(credentialBlob.value()));
76 credentialData = array.encode();
77 return true;
78}
79
80bool WritableIdentityCredential::initialize() {
81 optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
82 if (!keyPair) {
83 LOG(ERROR) << "Error creating credentialKey";
84 return false;
85 }
86
87 optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
88 if (!pubKey) {
89 LOG(ERROR) << "Error getting public part of credentialKey";
90 return false;
91 }
92 credentialPubKey_ = pubKey.value();
93
94 optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
95 if (!privKey) {
96 LOG(ERROR) << "Error getting private part of credentialKey";
97 return false;
98 }
99 credentialPrivKey_ = privKey.value();
100
101 optional<vector<uint8_t>> random = support::getRandom(16);
102 if (!random) {
103 LOG(ERROR) << "Error creating storageKey";
104 return false;
105 }
106 storageKey_ = random.value();
107
108 return true;
109}
110
111Return<void> WritableIdentityCredential::getAttestationCertificate(
112 const hidl_vec<uint8_t>& /* attestationChallenge */,
113 getAttestationCertificate_cb _hidl_cb) {
114 // For now, we dynamically generate an attestion key on each and every
115 // request and use that to sign CredentialKey. In a real implementation this
116 // would look very differently.
117 optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
118 if (!attestationKeyPair) {
119 _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
120 return Void();
121 }
122
123 optional<vector<uint8_t>> attestationPubKey =
124 support::ecKeyPairGetPublicKey(attestationKeyPair.value());
125 if (!attestationPubKey) {
126 _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
127 {});
128 return Void();
129 }
130
131 optional<vector<uint8_t>> attestationPrivKey =
132 support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
133 if (!attestationPrivKey) {
134 _hidl_cb(
135 support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
136 {});
137 return Void();
138 }
139
140 string serialDecimal;
141 string issuer;
142 string subject;
143 time_t validityNotBefore = time(nullptr);
144 time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
145
146 // First create a certificate for |credentialPubKey| which is signed by
147 // |attestationPrivKey|.
148 //
149 serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
150 issuer = "Android Open Source Project";
151 subject = "Android IdentityCredential CredentialKey";
152 optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
153 credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
154 validityNotBefore, validityNotAfter);
155 if (!credentialPubKeyCertificate) {
156 _hidl_cb(support::result(ResultCode::FAILED,
157 "Error creating certificate for credentialPubKey"),
158 {});
159 return Void();
160 }
161
162 // This is followed by a certificate for |attestationPubKey| self-signed by
163 // |attestationPrivKey|.
164 serialDecimal = "0"; // TODO: set serial
165 issuer = "Android Open Source Project";
166 subject = "Android IdentityCredential AttestationKey";
167 optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
168 attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
169 validityNotBefore, validityNotAfter);
170 if (!attestationKeyCertificate) {
171 _hidl_cb(support::result(ResultCode::FAILED,
172 "Error creating certificate for attestationPubKey"),
173 {});
174 return Void();
175 }
176
177 // Concatenate the certificates to form the chain.
178 vector<uint8_t> certificateChain;
179 certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
180 credentialPubKeyCertificate.value().end());
181 certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
182 attestationKeyCertificate.value().end());
183
184 _hidl_cb(support::resultOK(), certificateChain);
185 return Void();
186}
187
188Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
189 const hidl_vec<uint16_t>& entryCounts,
190 startPersonalization_cb _hidl_cb) {
191 numAccessControlProfileRemaining_ = accessControlProfileCount;
192 remainingEntryCounts_ = entryCounts;
193 entryNameSpace_ = "";
194
195 signedDataAccessControlProfiles_ = cppbor::Array();
196 signedDataNamespaces_ = cppbor::Map();
197 signedDataCurrentNamespace_ = cppbor::Array();
198
199 _hidl_cb(support::resultOK());
200 return Void();
201}
202
203Return<void> WritableIdentityCredential::addAccessControlProfile(
204 uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
205 uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
206 SecureAccessControlProfile profile;
207
208 if (numAccessControlProfileRemaining_ == 0) {
209 _hidl_cb(support::result(ResultCode::INVALID_DATA,
210 "numAccessControlProfileRemaining_ is 0 and expected non-zero"),
211 profile);
212 return Void();
213 }
214
215 // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
216 // be zero.
217 if (!userAuthenticationRequired && timeoutMillis != 0) {
218 _hidl_cb(support::result(ResultCode::INVALID_DATA,
219 "userAuthenticationRequired is false but timeout is non-zero"),
220 profile);
221 return Void();
222 }
223
224 profile.id = id;
225 profile.readerCertificate = readerCertificate;
226 profile.userAuthenticationRequired = userAuthenticationRequired;
227 profile.timeoutMillis = timeoutMillis;
228 profile.secureUserId = secureUserId;
229 optional<vector<uint8_t>> mac =
230 support::secureAccessControlProfileCalcMac(profile, storageKey_);
231 if (!mac) {
232 _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
233 return Void();
234 }
235 profile.mac = mac.value();
236
237 cppbor::Map profileMap;
238 profileMap.add("id", profile.id);
239 if (profile.readerCertificate.size() > 0) {
240 profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
241 }
242 if (profile.userAuthenticationRequired) {
243 profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
244 profileMap.add("timeoutMillis", profile.timeoutMillis);
245 }
246 signedDataAccessControlProfiles_.add(std::move(profileMap));
247
248 numAccessControlProfileRemaining_--;
249
250 _hidl_cb(support::resultOK(), profile);
251 return Void();
252}
253
254Return<void> WritableIdentityCredential::beginAddEntry(
255 const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
256 const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
257 if (numAccessControlProfileRemaining_ != 0) {
258 LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
259 << " and expected zero";
260 _hidl_cb(support::result(ResultCode::INVALID_DATA,
261 "numAccessControlProfileRemaining_ is %zd and expected zero",
262 numAccessControlProfileRemaining_));
263 return Void();
264 }
265
266 if (remainingEntryCounts_.size() == 0) {
267 _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
268 return Void();
269 }
270
271 // Handle initial beginEntry() call.
272 if (entryNameSpace_ == "") {
273 entryNameSpace_ = nameSpace;
274 }
275
276 // If the namespace changed...
277 if (nameSpace != entryNameSpace_) {
278 // Then check that all entries in the previous namespace have been added..
279 if (remainingEntryCounts_[0] != 0) {
280 _hidl_cb(support::result(ResultCode::INVALID_DATA,
281 "New namespace but %d entries remain to be added",
282 int(remainingEntryCounts_[0])));
283 return Void();
284 }
285 remainingEntryCounts_.erase(remainingEntryCounts_.begin());
286
287 if (signedDataCurrentNamespace_.size() > 0) {
288 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
289 signedDataCurrentNamespace_ = cppbor::Array();
290 }
291 } else {
292 // Same namespace...
293 if (remainingEntryCounts_[0] == 0) {
294 _hidl_cb(support::result(ResultCode::INVALID_DATA,
295 "Same namespace but no entries remain to be added"));
296 return Void();
297 }
298 remainingEntryCounts_[0] -= 1;
299 }
300
301 entryAdditionalData_ =
302 support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
303
304 entryRemainingBytes_ = entrySize;
305 entryNameSpace_ = nameSpace;
306 entryName_ = name;
307 entryAccessControlProfileIds_ = accessControlProfileIds;
308 entryBytes_.resize(0);
309 // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
310
311 _hidl_cb(support::resultOK());
312 return Void();
313}
314
315Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
316 addEntryValue_cb _hidl_cb) {
317 size_t contentSize = content.size();
318
319 if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
320 _hidl_cb(support::result(
321 ResultCode::INVALID_DATA,
322 "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
323 contentSize, IdentityCredentialStore::kGcmChunkSize),
324 {});
325 return Void();
326 }
327 if (contentSize > entryRemainingBytes_) {
328 _hidl_cb(support::result(ResultCode::INVALID_DATA,
329 "Passed in chunk of size %zd is bigger than remaining space "
330 "of size %zd",
331 contentSize, entryRemainingBytes_),
332 {});
333 return Void();
334 }
335
336 entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
337 entryRemainingBytes_ -= contentSize;
338 if (entryRemainingBytes_ > 0) {
339 if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
340 _hidl_cb(support::result(ResultCode::INVALID_DATA,
341 "Retrieved non-final chunk of size %zd but expected "
342 "kGcmChunkSize which is %zd",
343 contentSize, IdentityCredentialStore::kGcmChunkSize),
344 {});
345 return Void();
346 }
347 }
348
349 optional<vector<uint8_t>> nonce = support::getRandom(12);
350 if (!nonce) {
351 _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
352 return Void();
353 }
354 optional<vector<uint8_t>> encryptedContent =
355 support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
356 if (!encryptedContent) {
357 _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
358 return Void();
359 }
360
361 if (entryRemainingBytes_ == 0) {
362 // TODO: ideally do do this without parsing the data (but still validate data is valid
363 // CBOR).
364 auto [item, _, message] = cppbor::parse(entryBytes_);
365 if (item == nullptr) {
366 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
367 return Void();
368 }
369 cppbor::Map entryMap;
370 entryMap.add("name", entryName_);
371 entryMap.add("value", std::move(item));
372 cppbor::Array profileIdArray;
373 for (auto id : entryAccessControlProfileIds_) {
374 profileIdArray.add(id);
375 }
376 entryMap.add("accessControlProfiles", std::move(profileIdArray));
377 signedDataCurrentNamespace_.add(std::move(entryMap));
378 }
379
380 _hidl_cb(support::resultOK(), encryptedContent.value());
381 return Void();
382}
383
384Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
385 if (signedDataCurrentNamespace_.size() > 0) {
386 signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
387 }
388 cppbor::Array popArray;
389 popArray.add("ProofOfProvisioning")
390 .add(docType_)
391 .add(std::move(signedDataAccessControlProfiles_))
392 .add(std::move(signedDataNamespaces_))
393 .add(testCredential_);
394 vector<uint8_t> encodedCbor = popArray.encode();
395
396 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
397 encodedCbor, // payload
398 {}, // additionalData
399 {}); // certificateChain
400 if (!signature) {
401 _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
402 return Void();
403 }
404
405 vector<uint8_t> credentialKeys;
406 if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
407 _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
408 return Void();
409 }
410
411 vector<uint8_t> credentialData;
412 if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
413 : support::getHardwareBoundKey(),
414 docType_, testCredential_, credentialKeys, credentialData)) {
415 _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
416 return Void();
417 }
418
419 _hidl_cb(support::resultOK(), credentialData, signature.value());
420 return Void();
421}
422
423} // namespace implementation
424} // namespace identity
425} // namespace hardware
426} // namespace android