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