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