blob: c9df4fd74f0b35cb624df2af66cb0c6c37a0acd7 [file] [log] [blame]
David Zeuthen630de2a2020-05-11 14:04:54 -04001/*
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"
Joseph Jangdabb3c52021-09-01 16:50:09 +080018#include "EicCommon.h"
David Zeuthen630de2a2020-05-11 14:04:54 -040019
20bool 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
David Zeuthen49f2d252020-10-16 11:27:24 -040030bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
Joseph Jangdabb3c52021-09-01 16:50:09 +080031 size_t docTypeLength, const uint8_t* encryptedCredentialKeys,
David Zeuthen49f2d252020-10-16 11:27:24 -040032 size_t encryptedCredentialKeysSize) {
Joseph Jangdabb3c52021-09-01 16:50:09 +080033 uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
David Zeuthen49f2d252020-10-16 11:27:24 -040034
35 // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
36 // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
37 // to support loading all feature versions.
38 //
39 bool expectPopSha256 = false;
Joseph Jangdabb3c52021-09-01 16:50:09 +080040 if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) {
David Zeuthen49f2d252020-10-16 11:27:24 -040041 /* do nothing */
Joseph Jangdabb3c52021-09-01 16:50:09 +080042 } else if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) {
David Zeuthen49f2d252020-10-16 11:27:24 -040043 expectPopSha256 = true;
44 } else {
45 eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
46 return false;
47 }
48
49 eicMemSet(ctx, '\0', sizeof(EicProvisioning));
50 ctx->testCredential = testCredential;
51
52 if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
53 encryptedCredentialKeysSize,
54 // DocType is the additionalAuthenticatedData
Joseph Jangdabb3c52021-09-01 16:50:09 +080055 (const uint8_t*)docType, docTypeLength, credentialKeys)) {
David Zeuthen49f2d252020-10-16 11:27:24 -040056 eicDebug("Error decrypting CredentialKeys");
57 return false;
58 }
59
60 // It's supposed to look like this;
61 //
62 // Feature version 202009:
63 //
64 // CredentialKeys = [
65 // bstr, ; storageKey, a 128-bit AES key
66 // bstr, ; credentialPrivKey, the private key for credentialKey
67 // ]
68 //
69 // Feature version 202101:
70 //
71 // CredentialKeys = [
72 // bstr, ; storageKey, a 128-bit AES key
73 // bstr, ; credentialPrivKey, the private key for credentialKey
74 // bstr ; proofOfProvisioning SHA-256
75 // ]
76 //
77 // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
78 // SHA-256 is 32 bytes.
79 //
80 if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
81 credentialKeys[1] != 0x50 || // 16-byte bstr
82 credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) { // 32-byte bstr
83 eicDebug("Invalid CBOR for CredentialKeys");
84 return false;
85 }
86 if (expectPopSha256) {
87 if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
88 eicDebug("Invalid CBOR for CredentialKeys");
89 return false;
90 }
91 }
92 eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
93 eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
94 // Note: We don't care about the previous ProofOfProvisioning SHA-256
95 ctx->isUpdate = true;
96 return true;
97}
98
David Zeuthen630de2a2020-05-11 14:04:54 -040099bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
100 size_t challengeSize, const uint8_t* applicationId,
101 size_t applicationIdSize, uint8_t* publicKeyCert,
102 size_t* publicKeyCertSize) {
David Zeuthen49f2d252020-10-16 11:27:24 -0400103 if (ctx->isUpdate) {
104 eicDebug("Cannot create CredentialKey on update");
105 return false;
106 }
107
David Zeuthen630de2a2020-05-11 14:04:54 -0400108 if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
109 applicationId, applicationIdSize, ctx->testCredential,
110 publicKeyCert, publicKeyCertSize)) {
111 return false;
112 }
113 return true;
114}
115
116bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
117 const int* entryCounts, size_t numEntryCounts,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800118 const char* docType, size_t docTypeLength,
David Zeuthen630de2a2020-05-11 14:04:54 -0400119 size_t expectedProofOfProvisioningSize) {
120 if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
121 return false;
122 }
123 if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
124 return false;
125 }
126
127 ctx->numEntryCounts = numEntryCounts;
128 if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
129 return false;
130 }
131 for (size_t n = 0; n < numEntryCounts; n++) {
132 if (entryCounts[n] >= 256) {
133 return false;
134 }
135 ctx->entryCounts[n] = entryCounts[n];
136 }
137 ctx->curNamespace = -1;
138 ctx->curNamespaceNumProcessed = 0;
139
140 eicCborInit(&ctx->cbor, NULL, 0);
141
142 // What we're going to sign is the COSE ToBeSigned structure which
143 // looks like the following:
144 //
145 // Sig_structure = [
146 // context : "Signature" / "Signature1" / "CounterSignature",
147 // body_protected : empty_or_serialized_map,
148 // ? sign_protected : empty_or_serialized_map,
149 // external_aad : bstr,
150 // payload : bstr
151 // ]
152 //
153 eicCborAppendArray(&ctx->cbor, 4);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800154 eicCborAppendStringZ(&ctx->cbor, "Signature1");
David Zeuthen630de2a2020-05-11 14:04:54 -0400155
156 // The COSE Encoded protected headers is just a single field with
157 // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
158 // hard-code the CBOR encoding:
159 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
160 eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
161 sizeof(coseEncodedProtectedHeaders));
162
163 // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
164 // so external_aad is the empty bstr
165 static const uint8_t externalAad[0] = {};
166 eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
167
168 // For the payload, the _encoded_ form follows here. We handle this by simply
169 // opening a bstr, and then writing the CBOR. This requires us to know the
170 // size of said bstr, ahead of time.
171 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
172 ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
173
David Zeuthen49f2d252020-10-16 11:27:24 -0400174 eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
175 eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester);
176
David Zeuthen630de2a2020-05-11 14:04:54 -0400177 eicCborAppendArray(&ctx->cbor, 5);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800178 eicCborAppendStringZ(&ctx->cbor, "ProofOfProvisioning");
179 eicCborAppendString(&ctx->cbor, docType, docTypeLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400180
181 eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
182
183 return true;
184}
185
186bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
187 const uint8_t* readerCertificate,
188 size_t readerCertificateSize,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800189 bool userAuthenticationRequired,
190 uint64_t timeoutMillis, uint64_t secureUserId,
191 uint8_t outMac[28], uint8_t* scratchSpace,
192 size_t scratchSpaceSize) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400193 EicCbor cborBuilder;
Joseph Jangdabb3c52021-09-01 16:50:09 +0800194 eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
David Zeuthen630de2a2020-05-11 14:04:54 -0400195
196 if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
197 userAuthenticationRequired, timeoutMillis, secureUserId)) {
198 return false;
199 }
200
201 // Calculate and return MAC
202 uint8_t nonce[12];
203 if (!eicOpsRandom(nonce, 12)) {
204 return false;
205 }
206 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0, cborBuilder.buffer,
207 cborBuilder.size, outMac)) {
208 return false;
209 }
210
211 // The ACP CBOR in the provisioning receipt doesn't include secureUserId so build
212 // it again.
Joseph Jangdabb3c52021-09-01 16:50:09 +0800213 eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
David Zeuthen630de2a2020-05-11 14:04:54 -0400214 if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
215 userAuthenticationRequired, timeoutMillis,
216 0 /* secureUserId */)) {
217 return false;
218 }
219
220 // Append the CBOR from the local builder to the digester.
221 eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
222
223 return true;
224}
225
Joseph Jangdabb3c52021-09-01 16:50:09 +0800226bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const uint8_t* accessControlProfileIds,
David Zeuthen630de2a2020-05-11 14:04:54 -0400227 size_t numAccessControlProfileIds, const char* nameSpace,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800228 size_t nameSpaceLength, const char* name, size_t nameLength,
229 uint64_t entrySize, uint8_t* scratchSpace,
David Zeuthen630de2a2020-05-11 14:04:54 -0400230 size_t scratchSpaceSize) {
231 uint8_t* additionalDataCbor = scratchSpace;
232 const size_t additionalDataCborBufSize = scratchSpaceSize;
233 size_t additionalDataCborSize;
234
235 // We'll need to calc and store a digest of additionalData to check that it's the same
236 // additionalData being passed in for every eicProvisioningAddEntryValue() call...
237 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800238 nameSpace, nameSpaceLength, name, nameLength,
239 additionalDataCbor, additionalDataCborBufSize,
240 &additionalDataCborSize, ctx->additionalDataSha256)) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400241 return false;
242 }
243
244 if (ctx->curNamespace == -1) {
245 ctx->curNamespace = 0;
246 ctx->curNamespaceNumProcessed = 0;
247 // Opens the main map: { * Namespace => [ + Entry ] }
248 eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800249 eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400250 // Opens the per-namespace array: [ + Entry ]
251 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
252 }
253
254 if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
255 ctx->curNamespace += 1;
256 ctx->curNamespaceNumProcessed = 0;
Joseph Jangdabb3c52021-09-01 16:50:09 +0800257 eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400258 // Opens the per-namespace array: [ + Entry ]
259 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
260 }
261
262 eicCborAppendMap(&ctx->cbor, 3);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800263 eicCborAppendStringZ(&ctx->cbor, "name");
264 eicCborAppendString(&ctx->cbor, name, nameLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400265
266 ctx->curEntrySize = entrySize;
267 ctx->curEntryNumBytesReceived = 0;
268
Joseph Jangdabb3c52021-09-01 16:50:09 +0800269 eicCborAppendStringZ(&ctx->cbor, "value");
David Zeuthen630de2a2020-05-11 14:04:54 -0400270
271 ctx->curNamespaceNumProcessed += 1;
272 return true;
273}
274
Joseph Jangdabb3c52021-09-01 16:50:09 +0800275bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const uint8_t* accessControlProfileIds,
David Zeuthen630de2a2020-05-11 14:04:54 -0400276 size_t numAccessControlProfileIds, const char* nameSpace,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800277 size_t nameSpaceLength, const char* name, size_t nameLength,
278 const uint8_t* content, size_t contentSize,
David Zeuthen630de2a2020-05-11 14:04:54 -0400279 uint8_t* outEncryptedContent, uint8_t* scratchSpace,
280 size_t scratchSpaceSize) {
281 uint8_t* additionalDataCbor = scratchSpace;
282 const size_t additionalDataCborBufSize = scratchSpaceSize;
283 size_t additionalDataCborSize;
David Zeuthen630de2a2020-05-11 14:04:54 -0400284 uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
Joseph Jangdabb3c52021-09-01 16:50:09 +0800285
David Zeuthen630de2a2020-05-11 14:04:54 -0400286 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800287 nameSpace, nameSpaceLength, name, nameLength,
288 additionalDataCbor, additionalDataCborBufSize,
289 &additionalDataCborSize,
David Zeuthen630de2a2020-05-11 14:04:54 -0400290 calculatedSha256)) {
291 return false;
292 }
293 if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
294 eicDebug("SHA-256 mismatch of additionalData");
295 return false;
296 }
297
298 eicCborAppend(&ctx->cbor, content, contentSize);
299
300 uint8_t nonce[12];
301 if (!eicOpsRandom(nonce, 12)) {
302 return false;
303 }
304 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize, additionalDataCbor,
305 additionalDataCborSize, outEncryptedContent)) {
306 return false;
307 }
308
309 // If done with this entry, close the map
310 ctx->curEntryNumBytesReceived += contentSize;
311 if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800312 eicCborAppendStringZ(&ctx->cbor, "accessControlProfiles");
David Zeuthen630de2a2020-05-11 14:04:54 -0400313 eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
314 for (size_t n = 0; n < numAccessControlProfileIds; n++) {
315 eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
316 }
317 }
318 return true;
319}
320
321bool eicProvisioningFinishAddingEntries(
322 EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
323 uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
324
325 eicCborAppendBool(&ctx->cbor, ctx->testCredential);
326 eicCborFinal(&ctx->cbor, cborSha256);
327
328 // This verifies that the correct expectedProofOfProvisioningSize value was
329 // passed in at eicStartPersonalization() time.
330 if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
331 eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
332 return false;
333 }
334
335 if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
336 eicDebug("Error signing proofOfProvisioning");
337 return false;
338 }
339
340 return true;
341}
342
343bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800344 size_t docTypeLength,
David Zeuthen49f2d252020-10-16 11:27:24 -0400345 uint8_t* encryptedCredentialKeys,
346 size_t* encryptedCredentialKeysSize) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400347 EicCbor cbor;
David Zeuthen49f2d252020-10-16 11:27:24 -0400348 uint8_t cborBuf[86];
349
350 if (*encryptedCredentialKeysSize < 86 + 28) {
351 eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
352 return false;
353 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400354
355 eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
David Zeuthen49f2d252020-10-16 11:27:24 -0400356 eicCborAppendArray(&cbor, 3);
David Zeuthen630de2a2020-05-11 14:04:54 -0400357 eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
358 eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
David Zeuthen49f2d252020-10-16 11:27:24 -0400359 uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
360 eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
361 eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
David Zeuthen630de2a2020-05-11 14:04:54 -0400362 if (cbor.size > sizeof(cborBuf)) {
363 eicDebug("Exceeded buffer size");
364 return false;
365 }
366
367 uint8_t nonce[12];
368 if (!eicOpsRandom(nonce, 12)) {
369 eicDebug("Error getting random");
370 return false;
371 }
372 if (!eicOpsEncryptAes128Gcm(
373 eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size,
374 // DocType is the additionalAuthenticatedData
Joseph Jangdabb3c52021-09-01 16:50:09 +0800375 (const uint8_t*)docType, docTypeLength, encryptedCredentialKeys)) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400376 eicDebug("Error encrypting CredentialKeys");
377 return false;
378 }
David Zeuthen49f2d252020-10-16 11:27:24 -0400379 *encryptedCredentialKeysSize = cbor.size + 28;
David Zeuthen630de2a2020-05-11 14:04:54 -0400380
381 return true;
382}