1 /*
2 * Copyright 2020, 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 #include "EicProvisioning.h"
18 #include "EicCommon.h"
19
eicProvisioningInit(EicProvisioning * ctx,bool testCredential)20 bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
21 eicMemSet(ctx, '\0', sizeof(EicProvisioning));
22 ctx->testCredential = testCredential;
23 if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) {
24 return false;
25 }
26
27 return true;
28 }
29
eicProvisioningInitForUpdate(EicProvisioning * ctx,bool testCredential,const char * docType,size_t docTypeLength,const uint8_t * encryptedCredentialKeys,size_t encryptedCredentialKeysSize)30 bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential,
31 const char* docType, size_t docTypeLength,
32 const uint8_t* encryptedCredentialKeys,
33 size_t encryptedCredentialKeysSize) {
34 uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
35
36 // For feature version 202009 it's 52 bytes long and for feature version
37 // 202101 it's 86 bytes (the additional data is the ProofOfProvisioning
38 // SHA-256). We need to support loading all feature versions.
39 //
40 bool expectPopSha256 = false;
41 if (encryptedCredentialKeysSize ==
42 EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) {
43 /* do nothing */
44 } else if (encryptedCredentialKeysSize ==
45 EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) {
46 expectPopSha256 = true;
47 } else {
48 eicDebug("Unexpected size %zd for encryptedCredentialKeys",
49 encryptedCredentialKeysSize);
50 return false;
51 }
52
53 eicMemSet(ctx, '\0', sizeof(EicProvisioning));
54 ctx->testCredential = testCredential;
55
56 if (!eicOpsDecryptAes128Gcm(
57 eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
58 encryptedCredentialKeysSize,
59 // DocType is the additionalAuthenticatedData
60 (const uint8_t*)docType, docTypeLength, credentialKeys)) {
61 eicDebug("Error decrypting CredentialKeys");
62 return false;
63 }
64
65 // It's supposed to look like this;
66 //
67 // Feature version 202009:
68 //
69 // CredentialKeys = [
70 // bstr, ; storageKey, a 128-bit AES key
71 // bstr, ; credentialPrivKey, the private key for credentialKey
72 // ]
73 //
74 // Feature version 202101:
75 //
76 // CredentialKeys = [
77 // bstr, ; storageKey, a 128-bit AES key
78 // bstr, ; credentialPrivKey, the private key for credentialKey
79 // bstr ; proofOfProvisioning SHA-256
80 // ]
81 //
82 // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and
83 // proofOfProvisioning SHA-256 is 32 bytes.
84 //
85 if (credentialKeys[0] !=
86 (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
87 credentialKeys[1] != 0x50 || // 16-byte bstr
88 credentialKeys[18] != 0x58 ||
89 credentialKeys[19] != 0x20) { // 32-byte bstr
90 eicDebug("Invalid CBOR for CredentialKeys");
91 return false;
92 }
93 if (expectPopSha256) {
94 if (credentialKeys[52] != 0x58 ||
95 credentialKeys[53] != 0x20) { // 32-byte bstr
96 eicDebug("Invalid CBOR for CredentialKeys");
97 return false;
98 }
99 }
100 eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
101 eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20,
102 EIC_P256_PRIV_KEY_SIZE);
103 // Note: We don't care about the previous ProofOfProvisioning SHA-256
104 ctx->isUpdate = true;
105 return true;
106 }
107
eicProvisioningCreateCredentialKey(EicProvisioning * ctx,const uint8_t * challenge,size_t challengeSize,const uint8_t * applicationId,size_t applicationIdSize,uint8_t * publicKeyCert,size_t * publicKeyCertSize)108 bool eicProvisioningCreateCredentialKey(
109 EicProvisioning* ctx, const uint8_t* challenge, size_t challengeSize,
110 const uint8_t* applicationId, size_t applicationIdSize,
111 uint8_t* publicKeyCert, size_t* publicKeyCertSize) {
112 if (ctx->isUpdate) {
113 eicDebug("Cannot create CredentialKey on update");
114 return false;
115 }
116
117 if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge,
118 challengeSize, applicationId,
119 applicationIdSize, ctx->testCredential,
120 publicKeyCert, publicKeyCertSize)) {
121 return false;
122 }
123 return true;
124 }
125
eicProvisioningStartPersonalization(EicProvisioning * ctx,int accessControlProfileCount,const int * entryCounts,size_t numEntryCounts,const char * docType,size_t docTypeLength,size_t expectedProofOfProvisioningSize)126 bool eicProvisioningStartPersonalization(
127 EicProvisioning* ctx, int accessControlProfileCount, const int* entryCounts,
128 size_t numEntryCounts, const char* docType, size_t docTypeLength,
129 size_t expectedProofOfProvisioningSize) {
130 if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
131 return false;
132 }
133 if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
134 return false;
135 }
136
137 ctx->numEntryCounts = numEntryCounts;
138 if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
139 return false;
140 }
141 for (size_t n = 0; n < numEntryCounts; n++) {
142 if (entryCounts[n] >= 256) {
143 return false;
144 }
145 ctx->entryCounts[n] = entryCounts[n];
146 }
147 ctx->curNamespace = -1;
148 ctx->curNamespaceNumProcessed = 0;
149
150 eicCborInit(&ctx->cbor, NULL, 0);
151
152 // What we're going to sign is the COSE ToBeSigned structure which
153 // looks like the following:
154 //
155 // Sig_structure = [
156 // context : "Signature" / "Signature1" / "CounterSignature",
157 // body_protected : empty_or_serialized_map,
158 // ? sign_protected : empty_or_serialized_map,
159 // external_aad : bstr,
160 // payload : bstr
161 // ]
162 //
163 eicCborAppendArray(&ctx->cbor, 4);
164 eicCborAppendStringZ(&ctx->cbor, "Signature1");
165
166 // The COSE Encoded protected headers is just a single field with
167 // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicity we just
168 // hard-code the CBOR encoding:
169 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
170 eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
171 sizeof(coseEncodedProtectedHeaders));
172
173 // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
174 // so external_aad is the empty bstr
175 static const uint8_t externalAad[0] = {};
176 eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
177
178 // For the payload, the _encoded_ form follows here. We handle this by simply
179 // opening a bstr, and then writing the CBOR. This requires us to know the
180 // size of said bstr, ahead of time.
181 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING,
182 expectedProofOfProvisioningSize);
183 ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
184
185 eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
186 eicCborEnableSecondaryDigesterSha256(&ctx->cbor,
187 &ctx->proofOfProvisioningDigester);
188
189 eicCborAppendArray(&ctx->cbor, 5);
190 eicCborAppendStringZ(&ctx->cbor, "ProofOfProvisioning");
191 eicCborAppendString(&ctx->cbor, docType, docTypeLength);
192
193 eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
194
195 return true;
196 }
197
eicProvisioningAddAccessControlProfile(EicProvisioning * ctx,int id,const uint8_t * readerCertificate,size_t readerCertificateSize,bool userAuthenticationRequired,uint64_t timeoutMillis,uint64_t secureUserId,uint8_t outMac[28],uint8_t * scratchSpace,size_t scratchSpaceSize)198 bool eicProvisioningAddAccessControlProfile(
199 EicProvisioning* ctx, int id, const uint8_t* readerCertificate,
200 size_t readerCertificateSize, bool userAuthenticationRequired,
201 uint64_t timeoutMillis, uint64_t secureUserId, uint8_t outMac[28],
202 uint8_t* scratchSpace, size_t scratchSpaceSize) {
203 EicCbor cborBuilder;
204 eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
205
206 if (!eicCborCalcAccessControl(
207 &cborBuilder, id, readerCertificate, readerCertificateSize,
208 userAuthenticationRequired, timeoutMillis, secureUserId)) {
209 return false;
210 }
211
212 // Calculate and return MAC
213 uint8_t nonce[12];
214 if (!eicOpsRandom(nonce, 12)) {
215 return false;
216 }
217 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0,
218 cborBuilder.buffer, cborBuilder.size, outMac)) {
219 return false;
220 }
221
222 // The ACP CBOR in the provisioning receipt doesn't include secureUserId so
223 // build it again.
224 eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
225 if (!eicCborCalcAccessControl(
226 &cborBuilder, id, readerCertificate, readerCertificateSize,
227 userAuthenticationRequired, timeoutMillis, 0 /* secureUserId */)) {
228 return false;
229 }
230
231 // Append the CBOR from the local builder to the digester.
232 eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
233
234 return true;
235 }
236
eicProvisioningBeginAddEntry(EicProvisioning * ctx,const uint8_t * accessControlProfileIds,size_t numAccessControlProfileIds,const char * nameSpace,size_t nameSpaceLength,const char * name,size_t nameLength,uint64_t entrySize,uint8_t * scratchSpace,size_t scratchSpaceSize)237 bool eicProvisioningBeginAddEntry(EicProvisioning* ctx,
238 const uint8_t* accessControlProfileIds,
239 size_t numAccessControlProfileIds,
240 const char* nameSpace, size_t nameSpaceLength,
241 const char* name, size_t nameLength,
242 uint64_t entrySize, uint8_t* scratchSpace,
243 size_t scratchSpaceSize) {
244 uint8_t* additionalDataCbor = scratchSpace;
245 const size_t additionalDataCborBufSize = scratchSpaceSize;
246 size_t additionalDataCborSize;
247
248 // We'll need to calc and store a digest of additionalData to check that it's
249 // the same additionalData being passed in for every
250 // eicProvisioningAddEntryValue() call...
251 if (!eicCborCalcEntryAdditionalData(
252 accessControlProfileIds, numAccessControlProfileIds, nameSpace,
253 nameSpaceLength, name, nameLength, additionalDataCbor,
254 additionalDataCborBufSize, &additionalDataCborSize,
255 ctx->additionalDataSha256)) {
256 return false;
257 }
258
259 if (ctx->curNamespace == -1) {
260 ctx->curNamespace = 0;
261 ctx->curNamespaceNumProcessed = 0;
262 // Opens the main map: { * Namespace => [ + Entry ] }
263 eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
264 eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
265 // Opens the per-namespace array: [ + Entry ]
266 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
267 }
268
269 if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
270 ctx->curNamespace += 1;
271 ctx->curNamespaceNumProcessed = 0;
272 eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
273 // Opens the per-namespace array: [ + Entry ]
274 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
275 }
276
277 eicCborAppendMap(&ctx->cbor, 3);
278 eicCborAppendStringZ(&ctx->cbor, "name");
279 eicCborAppendString(&ctx->cbor, name, nameLength);
280
281 ctx->curEntrySize = entrySize;
282 ctx->curEntryNumBytesReceived = 0;
283
284 eicCborAppendStringZ(&ctx->cbor, "value");
285
286 ctx->curNamespaceNumProcessed += 1;
287 return true;
288 }
289
eicProvisioningAddEntryValue(EicProvisioning * ctx,const uint8_t * accessControlProfileIds,size_t numAccessControlProfileIds,const char * nameSpace,size_t nameSpaceLength,const char * name,size_t nameLength,const uint8_t * content,size_t contentSize,uint8_t * outEncryptedContent,uint8_t * scratchSpace,size_t scratchSpaceSize)290 bool eicProvisioningAddEntryValue(
291 EicProvisioning* ctx, const uint8_t* accessControlProfileIds,
292 size_t numAccessControlProfileIds, const char* nameSpace,
293 size_t nameSpaceLength, const char* name, size_t nameLength,
294 const uint8_t* content, size_t contentSize, uint8_t* outEncryptedContent,
295 uint8_t* scratchSpace, size_t scratchSpaceSize) {
296 uint8_t* additionalDataCbor = scratchSpace;
297 const size_t additionalDataCborBufSize = scratchSpaceSize;
298 size_t additionalDataCborSize;
299 uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
300
301 if (!eicCborCalcEntryAdditionalData(
302 accessControlProfileIds, numAccessControlProfileIds, nameSpace,
303 nameSpaceLength, name, nameLength, additionalDataCbor,
304 additionalDataCborBufSize, &additionalDataCborSize,
305 calculatedSha256)) {
306 return false;
307 }
308 if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256,
309 EIC_SHA256_DIGEST_SIZE) != 0) {
310 eicDebug("SHA-256 mismatch of additionalData");
311 return false;
312 }
313
314 eicCborAppend(&ctx->cbor, content, contentSize);
315
316 uint8_t nonce[12];
317 if (!eicOpsRandom(nonce, 12)) {
318 return false;
319 }
320 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize,
321 additionalDataCbor, additionalDataCborSize,
322 outEncryptedContent)) {
323 return false;
324 }
325
326 // If done with this entry, close the map
327 ctx->curEntryNumBytesReceived += contentSize;
328 if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
329 eicCborAppendStringZ(&ctx->cbor, "accessControlProfiles");
330 eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
331 for (size_t n = 0; n < numAccessControlProfileIds; n++) {
332 eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
333 }
334 }
335 return true;
336 }
337
eicProvisioningFinishAddingEntries(EicProvisioning * ctx,uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE])338 bool eicProvisioningFinishAddingEntries(
339 EicProvisioning* ctx,
340 uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
341 uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
342
343 eicCborAppendBool(&ctx->cbor, ctx->testCredential);
344 eicCborFinal(&ctx->cbor, cborSha256);
345
346 // This verifies that the correct expectedProofOfProvisioningSize value was
347 // passed in at eicStartPersonalization() time.
348 if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
349 eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size,
350 ctx->expectedCborSizeAtEnd);
351 return false;
352 }
353
354 if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256,
355 signatureOfToBeSigned)) {
356 eicDebug("Error signing proofOfProvisioning");
357 return false;
358 }
359
360 return true;
361 }
362
eicProvisioningFinishGetCredentialData(EicProvisioning * ctx,const char * docType,size_t docTypeLength,uint8_t * encryptedCredentialKeys,size_t * encryptedCredentialKeysSize)363 bool eicProvisioningFinishGetCredentialData(
364 EicProvisioning* ctx, const char* docType, size_t docTypeLength,
365 uint8_t* encryptedCredentialKeys, size_t* encryptedCredentialKeysSize) {
366 EicCbor cbor;
367 uint8_t cborBuf[86];
368
369 if (*encryptedCredentialKeysSize < 86 + 28) {
370 eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
371 return false;
372 }
373
374 eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
375 eicCborAppendArray(&cbor, 3);
376 eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
377 eicCborAppendByteString(&cbor, ctx->credentialPrivateKey,
378 EIC_P256_PRIV_KEY_SIZE);
379 uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
380 eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
381 eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
382 if (cbor.size > sizeof(cborBuf)) {
383 eicDebug("Exceeded buffer size");
384 return false;
385 }
386
387 uint8_t nonce[12];
388 if (!eicOpsRandom(nonce, 12)) {
389 eicDebug("Error getting random");
390 return false;
391 }
392 if (!eicOpsEncryptAes128Gcm(eicOpsGetHardwareBoundKey(ctx->testCredential),
393 nonce, cborBuf, cbor.size,
394 // DocType is the additionalAuthenticatedData
395 (const uint8_t*)docType, docTypeLength,
396 encryptedCredentialKeys)) {
397 eicDebug("Error encrypting CredentialKeys");
398 return false;
399 }
400 *encryptedCredentialKeysSize = cbor.size + 28;
401
402 return true;
403 }
404