blob: f16605cfad92d9fc482ae9077731b8741fc36628 [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"
18
19bool 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
29bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
30 size_t challengeSize, const uint8_t* applicationId,
31 size_t applicationIdSize, uint8_t* publicKeyCert,
32 size_t* publicKeyCertSize) {
33 if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
34 applicationId, applicationIdSize, ctx->testCredential,
35 publicKeyCert, publicKeyCertSize)) {
36 return false;
37 }
38 return true;
39}
40
41bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
42 const int* entryCounts, size_t numEntryCounts,
43 const char* docType,
44 size_t expectedProofOfProvisioningSize) {
45 if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
46 return false;
47 }
48 if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
49 return false;
50 }
51
52 ctx->numEntryCounts = numEntryCounts;
53 if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
54 return false;
55 }
56 for (size_t n = 0; n < numEntryCounts; n++) {
57 if (entryCounts[n] >= 256) {
58 return false;
59 }
60 ctx->entryCounts[n] = entryCounts[n];
61 }
62 ctx->curNamespace = -1;
63 ctx->curNamespaceNumProcessed = 0;
64
65 eicCborInit(&ctx->cbor, NULL, 0);
66
67 // What we're going to sign is the COSE ToBeSigned structure which
68 // looks like the following:
69 //
70 // Sig_structure = [
71 // context : "Signature" / "Signature1" / "CounterSignature",
72 // body_protected : empty_or_serialized_map,
73 // ? sign_protected : empty_or_serialized_map,
74 // external_aad : bstr,
75 // payload : bstr
76 // ]
77 //
78 eicCborAppendArray(&ctx->cbor, 4);
79 eicCborAppendString(&ctx->cbor, "Signature1");
80
81 // The COSE Encoded protected headers is just a single field with
82 // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
83 // hard-code the CBOR encoding:
84 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
85 eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
86 sizeof(coseEncodedProtectedHeaders));
87
88 // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
89 // so external_aad is the empty bstr
90 static const uint8_t externalAad[0] = {};
91 eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
92
93 // For the payload, the _encoded_ form follows here. We handle this by simply
94 // opening a bstr, and then writing the CBOR. This requires us to know the
95 // size of said bstr, ahead of time.
96 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
97 ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
98
99 eicCborAppendArray(&ctx->cbor, 5);
100 eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
101 eicCborAppendString(&ctx->cbor, docType);
102
103 eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
104
105 return true;
106}
107
108bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
109 const uint8_t* readerCertificate,
110 size_t readerCertificateSize,
111 bool userAuthenticationRequired, uint64_t timeoutMillis,
112 uint64_t secureUserId, uint8_t outMac[28]) {
113 uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
114 EicCbor cborBuilder;
115
116 eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
117
118 if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
119 userAuthenticationRequired, timeoutMillis, secureUserId)) {
120 return false;
121 }
122
123 // Calculate and return MAC
124 uint8_t nonce[12];
125 if (!eicOpsRandom(nonce, 12)) {
126 return false;
127 }
128 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0, cborBuilder.buffer,
129 cborBuilder.size, outMac)) {
130 return false;
131 }
132
133 // The ACP CBOR in the provisioning receipt doesn't include secureUserId so build
134 // it again.
135 eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
136 if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
137 userAuthenticationRequired, timeoutMillis,
138 0 /* secureUserId */)) {
139 return false;
140 }
141
142 // Append the CBOR from the local builder to the digester.
143 eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
144
145 return true;
146}
147
148bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
149 size_t numAccessControlProfileIds, const char* nameSpace,
150 const char* name, uint64_t entrySize, uint8_t* scratchSpace,
151 size_t scratchSpaceSize) {
152 uint8_t* additionalDataCbor = scratchSpace;
153 const size_t additionalDataCborBufSize = scratchSpaceSize;
154 size_t additionalDataCborSize;
155
156 // We'll need to calc and store a digest of additionalData to check that it's the same
157 // additionalData being passed in for every eicProvisioningAddEntryValue() call...
158 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
159 nameSpace, name, additionalDataCbor,
160 additionalDataCborBufSize, &additionalDataCborSize,
161 ctx->additionalDataSha256)) {
162 return false;
163 }
164
165 if (ctx->curNamespace == -1) {
166 ctx->curNamespace = 0;
167 ctx->curNamespaceNumProcessed = 0;
168 // Opens the main map: { * Namespace => [ + Entry ] }
169 eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
170 eicCborAppendString(&ctx->cbor, nameSpace);
171 // Opens the per-namespace array: [ + Entry ]
172 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
173 }
174
175 if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
176 ctx->curNamespace += 1;
177 ctx->curNamespaceNumProcessed = 0;
178 eicCborAppendString(&ctx->cbor, nameSpace);
179 // Opens the per-namespace array: [ + Entry ]
180 eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
181 }
182
183 eicCborAppendMap(&ctx->cbor, 3);
184 eicCborAppendString(&ctx->cbor, "name");
185 eicCborAppendString(&ctx->cbor, name);
186
187 ctx->curEntrySize = entrySize;
188 ctx->curEntryNumBytesReceived = 0;
189
190 eicCborAppendString(&ctx->cbor, "value");
191
192 ctx->curNamespaceNumProcessed += 1;
193 return true;
194}
195
196bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
197 size_t numAccessControlProfileIds, const char* nameSpace,
198 const char* name, const uint8_t* content, size_t contentSize,
199 uint8_t* outEncryptedContent, uint8_t* scratchSpace,
200 size_t scratchSpaceSize) {
201 uint8_t* additionalDataCbor = scratchSpace;
202 const size_t additionalDataCborBufSize = scratchSpaceSize;
203 size_t additionalDataCborSize;
204
205 uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
206 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
207 nameSpace, name, additionalDataCbor,
208 additionalDataCborBufSize, &additionalDataCborSize,
209 calculatedSha256)) {
210 return false;
211 }
212 if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
213 eicDebug("SHA-256 mismatch of additionalData");
214 return false;
215 }
216
217 eicCborAppend(&ctx->cbor, content, contentSize);
218
219 uint8_t nonce[12];
220 if (!eicOpsRandom(nonce, 12)) {
221 return false;
222 }
223 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize, additionalDataCbor,
224 additionalDataCborSize, outEncryptedContent)) {
225 return false;
226 }
227
228 // If done with this entry, close the map
229 ctx->curEntryNumBytesReceived += contentSize;
230 if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
231 eicCborAppendString(&ctx->cbor, "accessControlProfiles");
232 eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
233 for (size_t n = 0; n < numAccessControlProfileIds; n++) {
234 eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
235 }
236 }
237 return true;
238}
239
240bool eicProvisioningFinishAddingEntries(
241 EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
242 uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
243
244 eicCborAppendBool(&ctx->cbor, ctx->testCredential);
245 eicCborFinal(&ctx->cbor, cborSha256);
246
247 // This verifies that the correct expectedProofOfProvisioningSize value was
248 // passed in at eicStartPersonalization() time.
249 if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
250 eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
251 return false;
252 }
253
254 if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
255 eicDebug("Error signing proofOfProvisioning");
256 return false;
257 }
258
259 return true;
260}
261
262bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
263 uint8_t encryptedCredentialKeys[80]) {
264 EicCbor cbor;
265 uint8_t cborBuf[52];
266
267 eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
268 eicCborAppendArray(&cbor, 2);
269 eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
270 eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
271 if (cbor.size > sizeof(cborBuf)) {
272 eicDebug("Exceeded buffer size");
273 return false;
274 }
275
276 uint8_t nonce[12];
277 if (!eicOpsRandom(nonce, 12)) {
278 eicDebug("Error getting random");
279 return false;
280 }
281 if (!eicOpsEncryptAes128Gcm(
282 eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size,
283 // DocType is the additionalAuthenticatedData
284 (const uint8_t*)docType, eicStrLen(docType), encryptedCredentialKeys)) {
285 eicDebug("Error encrypting CredentialKeys");
286 return false;
287 }
288
289 return true;
290}