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