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