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