blob: d3f5556f66949123af32b8f2e2ca02a3ca06b94b [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 "EicPresentation.h"
18
19#include <inttypes.h>
20
21bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
22 const uint8_t encryptedCredentialKeys[80]) {
23 uint8_t credentialKeys[52];
24
25 eicMemSet(ctx, '\0', sizeof(EicPresentation));
26
27 if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
28 80,
29 // DocType is the additionalAuthenticatedData
30 (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
31 eicDebug("Error decrypting CredentialKeys");
32 return false;
33 }
34
35 // It's supposed to look like this;
36 //
37 // CredentialKeys = [
38 // bstr, ; storageKey, a 128-bit AES key
39 // bstr ; credentialPrivKey, the private key for credentialKey
40 // ]
41 //
42 // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
43 //
44 // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
45 // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
46 // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
47 //
48 if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
49 credentialKeys[19] != 0x20) {
50 eicDebug("Invalid CBOR for CredentialKeys");
51 return false;
52 }
53 eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
54 eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
55 ctx->testCredential = testCredential;
56 return true;
57}
58
59bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
60 uint8_t* publicKeyCert, size_t* publicKeyCertSize,
61 uint8_t signingKeyBlob[60]) {
62 uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
63 uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
64
65 if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
66 eicDebug("Error creating signing key");
67 return false;
68 }
69
70 const int secondsInOneYear = 365 * 24 * 60 * 60;
71 time_t validityNotBefore = now;
72 time_t validityNotAfter = now + secondsInOneYear; // One year from now.
73 if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
74 "Android Identity Credential Key", // issuer CN
75 "Android Identity Credential Authentication Key", // subject CN
76 validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
77 eicDebug("Error creating certificate for signing key");
78 return false;
79 }
80
81 uint8_t nonce[12];
82 if (!eicOpsRandom(nonce, 12)) {
83 eicDebug("Error getting random");
84 return false;
85 }
86 if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv),
87 // DocType is the additionalAuthenticatedData
88 (const uint8_t*)docType, eicStrLen(docType), signingKeyBlob)) {
89 eicDebug("Error encrypting signing key");
90 return false;
91 }
92
93 return true;
94}
95
96bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
97 uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) {
98 uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE];
99 if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ephemeralPublicKey)) {
100 eicDebug("Error creating ephemeral key");
101 return false;
102 }
103 eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey, EIC_P256_PRIV_KEY_SIZE);
104 return true;
105}
106
107bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge) {
108 do {
109 if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(uint64_t))) {
110 eicDebug("Failed generating random challenge");
111 return false;
112 }
113 } while (ctx->authChallenge == 0);
114 eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge);
115 *authChallenge = ctx->authChallenge;
116 return true;
117}
118
119// From "COSE Algorithms" registry
120//
121#define COSE_ALG_ECDSA_256 -7
122
123bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
124 size_t sessionTranscriptSize,
125 const uint8_t* requestMessage, size_t requestMessageSize,
126 int coseSignAlg,
127 const uint8_t* readerSignatureOfToBeSigned,
128 size_t readerSignatureOfToBeSignedSize) {
129 if (ctx->readerPublicKeySize == 0) {
130 eicDebug("No public key for reader");
131 return false;
132 }
133
134 // Right now we only support ECDSA with SHA-256 (e.g. ES256).
135 //
136 if (coseSignAlg != COSE_ALG_ECDSA_256) {
137 eicDebug(
138 "COSE Signature algorithm for reader signature is %d, "
139 "only ECDSA with SHA-256 is supported right now",
140 coseSignAlg);
141 return false;
142 }
143
144 // What we're going to verify is the COSE ToBeSigned structure which
145 // looks like the following:
146 //
147 // Sig_structure = [
148 // context : "Signature" / "Signature1" / "CounterSignature",
149 // body_protected : empty_or_serialized_map,
150 // ? sign_protected : empty_or_serialized_map,
151 // external_aad : bstr,
152 // payload : bstr
153 // ]
154 //
155 // So we're going to build that CBOR...
156 //
157 EicCbor cbor;
158 eicCborInit(&cbor, NULL, 0);
159 eicCborAppendArray(&cbor, 4);
160 eicCborAppendString(&cbor, "Signature1");
161
162 // The COSE Encoded protected headers is just a single field with
163 // COSE_LABEL_ALG (1) -> coseSignAlg (e.g. -7). For simplicitly we just
164 // hard-code the CBOR encoding:
165 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
166 eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
167 sizeof(coseEncodedProtectedHeaders));
168
169 // External_aad is the empty bstr
170 static const uint8_t externalAad[0] = {};
171 eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
172
173 // For the payload, the _encoded_ form follows here. We handle this by simply
174 // opening a bstr, and then writing the CBOR. This requires us to know the
175 // size of said bstr, ahead of time... the CBOR to be written is
176 //
177 // ReaderAuthentication = [
178 // "ReaderAuthentication",
179 // SessionTranscript,
180 // ItemsRequestBytes
181 // ]
182 //
183 // ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
184 //
185 // ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
186 //
187 // which is easily calculated below
188 //
189 size_t calculatedSize = 0;
190 calculatedSize += 1; // Array of size 3
191 calculatedSize += 1; // "ReaderAuthentication" less than 24 bytes
192 calculatedSize += sizeof("ReaderAuthentication") - 1; // Don't include trailing NUL
193 calculatedSize += sessionTranscriptSize; // Already CBOR encoded
194 calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
195 calculatedSize += 1 + eicCborAdditionalLengthBytesFor(requestMessageSize);
196 calculatedSize += requestMessageSize;
197
198 // However note that we're authenticating ReaderAuthenticationBytes which
199 // is a tagged bstr of the bytes of ReaderAuthentication. So need to get
200 // that in front.
201 size_t rabCalculatedSize = 0;
202 rabCalculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
203 rabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
204 rabCalculatedSize += calculatedSize;
205
206 // Begin the bytestring for ReaderAuthenticationBytes;
207 eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, rabCalculatedSize);
208
209 eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
210
211 // Begins the bytestring for ReaderAuthentication;
212 eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
213
214 // And now that we know the size, let's fill it in...
215 //
216 size_t payloadOffset = cbor.size;
217 eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3);
218 eicCborAppendString(&cbor, "ReaderAuthentication");
219 eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize);
220 eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
221 eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize);
222 eicCborAppend(&cbor, requestMessage, requestMessageSize);
223
224 if (cbor.size != payloadOffset + calculatedSize) {
225 eicDebug("CBOR size is %zd but we expected %zd", cbor.size, payloadOffset + calculatedSize);
226 return false;
227 }
228 uint8_t toBeSignedDigest[EIC_SHA256_DIGEST_SIZE];
229 eicCborFinal(&cbor, toBeSignedDigest);
230
231 if (!eicOpsEcDsaVerifyWithPublicKey(
232 toBeSignedDigest, EIC_SHA256_DIGEST_SIZE, readerSignatureOfToBeSigned,
233 readerSignatureOfToBeSignedSize, ctx->readerPublicKey, ctx->readerPublicKeySize)) {
234 eicDebug("Request message is not signed by public key");
235 return false;
236 }
237 ctx->requestMessageValidated = true;
238 return true;
239}
240
241// Validates the next certificate in the reader certificate chain.
242bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
243 size_t certX509Size) {
244 // If we had a previous certificate, use its public key to validate this certificate.
245 if (ctx->readerPublicKeySize > 0) {
246 if (!eicOpsX509CertSignedByPublicKey(certX509, certX509Size, ctx->readerPublicKey,
247 ctx->readerPublicKeySize)) {
248 eicDebug("Certificate is not signed by public key in the previous certificate");
249 return false;
250 }
251 }
252
253 // Store the key of this certificate, this is used to validate the next certificate
254 // and also ACPs with certificates that use the same public key...
255 ctx->readerPublicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
256 if (!eicOpsX509GetPublicKey(certX509, certX509Size, ctx->readerPublicKey,
257 &ctx->readerPublicKeySize)) {
258 eicDebug("Error extracting public key from certificate");
259 return false;
260 }
261 if (ctx->readerPublicKeySize == 0) {
262 eicDebug("Zero-length public key in certificate");
263 return false;
264 }
265
266 return true;
267}
268
269bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
270 uint64_t authenticatorId, int hardwareAuthenticatorType,
271 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
272 uint64_t verificationTokenChallenge,
273 uint64_t verificationTokenTimestamp,
274 int verificationTokenSecurityLevel,
275 const uint8_t* verificationTokenMac,
276 size_t verificationTokenMacSize) {
277 if (!eicOpsValidateAuthToken(
278 challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac,
279 macSize, verificationTokenChallenge, verificationTokenTimestamp,
280 verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) {
281 return false;
282 }
283 ctx->authTokenChallenge = challenge;
284 ctx->authTokenSecureUserId = secureUserId;
285 ctx->authTokenTimestamp = timeStamp;
286 ctx->verificationTokenTimestamp = verificationTokenTimestamp;
287 return true;
288}
289
290static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired, int timeoutMillis,
291 uint64_t secureUserId) {
292 if (!userAuthenticationRequired) {
293 return true;
294 }
295
296 if (secureUserId != ctx->authTokenSecureUserId) {
297 eicDebug("secureUserId in profile differs from userId in authToken");
298 return false;
299 }
300
301 if (timeoutMillis == 0) {
302 if (ctx->authTokenChallenge == 0) {
303 eicDebug("No challenge in authToken");
304 return false;
305 }
306
307 // If we didn't create a challenge, too bad but user auth with
308 // timeoutMillis set to 0 needs it.
309 if (ctx->authChallenge == 0) {
310 eicDebug("No challenge was created for this session");
311 return false;
312 }
313 if (ctx->authTokenChallenge != ctx->authChallenge) {
314 eicDebug("Challenge in authToken (%" PRIu64
315 ") doesn't match the challenge "
316 "that was created (%" PRIu64 ") for this session",
317 ctx->authTokenChallenge, ctx->authChallenge);
318 return false;
319 }
320 }
321
322 uint64_t now = ctx->verificationTokenTimestamp;
323 if (ctx->authTokenTimestamp > now) {
324 eicDebug("Timestamp in authToken is in the future");
325 return false;
326 }
327
328 if (timeoutMillis > 0) {
329 if (now > ctx->authTokenTimestamp + timeoutMillis) {
330 eicDebug("Deadline for authToken is in the past");
331 return false;
332 }
333 }
334
335 return true;
336}
337
338static bool checkReaderAuth(EicPresentation* ctx, const uint8_t* readerCertificate,
339 size_t readerCertificateSize) {
340 uint8_t publicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
341 size_t publicKeySize;
342
343 if (readerCertificateSize == 0) {
344 return true;
345 }
346
347 // Remember in this case certificate equality is done by comparing public
348 // keys, not bitwise comparison of the certificates.
349 //
350 publicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
351 if (!eicOpsX509GetPublicKey(readerCertificate, readerCertificateSize, publicKey,
352 &publicKeySize)) {
353 eicDebug("Error extracting public key from certificate");
354 return false;
355 }
356 if (publicKeySize == 0) {
357 eicDebug("Zero-length public key in certificate");
358 return false;
359 }
360
361 if ((ctx->readerPublicKeySize != publicKeySize) ||
362 (eicCryptoMemCmp(ctx->readerPublicKey, publicKey, ctx->readerPublicKeySize) != 0)) {
363 return false;
364 }
365 return true;
366}
367
368// Note: This function returns false _only_ if an error occurred check for access, _not_
369// whether access is granted. Whether access is granted is returned in |accessGranted|.
370//
371bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
372 const uint8_t* readerCertificate,
373 size_t readerCertificateSize,
374 bool userAuthenticationRequired, int timeoutMillis,
375 uint64_t secureUserId, const uint8_t mac[28],
376 bool* accessGranted) {
377 *accessGranted = false;
378
379 if (id < 0 || id >= 32) {
380 eicDebug("id value of %d is out of allowed range [0, 32[", id);
381 return false;
382 }
383
384 // Validate the MAC
385 uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
386 EicCbor cborBuilder;
387 eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
388 if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
389 userAuthenticationRequired, timeoutMillis, secureUserId)) {
390 return false;
391 }
392 if (!eicOpsDecryptAes128Gcm(ctx->storageKey, mac, 28, cborBuilder.buffer, cborBuilder.size,
393 NULL)) {
394 eicDebug("MAC for AccessControlProfile doesn't match");
395 return false;
396 }
397
398 bool passedUserAuth =
399 checkUserAuth(ctx, userAuthenticationRequired, timeoutMillis, secureUserId);
400 bool passedReaderAuth = checkReaderAuth(ctx, readerCertificate, readerCertificateSize);
401
402 ctx->accessControlProfileMaskValidated |= (1 << id);
403 if (readerCertificateSize > 0) {
404 ctx->accessControlProfileMaskUsesReaderAuth |= (1 << id);
405 }
406 if (!passedReaderAuth) {
407 ctx->accessControlProfileMaskFailedReaderAuth |= (1 << id);
408 }
409 if (!passedUserAuth) {
410 ctx->accessControlProfileMaskFailedUserAuth |= (1 << id);
411 }
412
413 if (passedUserAuth && passedReaderAuth) {
414 *accessGranted = true;
415 eicDebug("Access granted for id %d", id);
416 }
417 return true;
418}
419
420bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
421 size_t sessionTranscriptSize,
422 const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
423 const uint8_t signingKeyBlob[60], const char* docType,
424 unsigned int numNamespacesWithValues,
425 size_t expectedDeviceNamespacesSize) {
426 uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
427 if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
428 eicStrLen(docType), signingKeyPriv)) {
429 eicDebug("Error decrypting signingKeyBlob");
430 return false;
431 }
432
433 uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
434 if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
435 eicDebug("ECDH failed");
436 return false;
437 }
438
439 EicCbor cbor;
440 eicCborInit(&cbor, NULL, 0);
441 eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
442 eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
443 uint8_t salt[EIC_SHA256_DIGEST_SIZE];
444 eicCborFinal(&cbor, salt);
445
446 const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
447 uint8_t derivedKey[32];
448 if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt), info, sizeof(info),
449 derivedKey, sizeof(derivedKey))) {
450 eicDebug("HKDF failed");
451 return false;
452 }
453
454 eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
455 ctx->buildCbor = true;
456
457 // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
458 // structure which looks like the following:
459 //
460 // MAC_structure = [
461 // context : "MAC" / "MAC0",
462 // protected : empty_or_serialized_map,
463 // external_aad : bstr,
464 // payload : bstr
465 // ]
466 //
467 eicCborAppendArray(&ctx->cbor, 4);
468 eicCborAppendString(&ctx->cbor, "MAC0");
469
470 // The COSE Encoded protected headers is just a single field with
471 // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
472 // hard-code the CBOR encoding:
473 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
474 eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
475 sizeof(coseEncodedProtectedHeaders));
476
477 // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
478 // so external_aad is the empty bstr
479 static const uint8_t externalAad[0] = {};
480 eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
481
482 // For the payload, the _encoded_ form follows here. We handle this by simply
483 // opening a bstr, and then writing the CBOR. This requires us to know the
484 // size of said bstr, ahead of time... the CBOR to be written is
485 //
486 // DeviceAuthentication = [
487 // "DeviceAuthentication",
488 // SessionTranscript,
489 // DocType, ; DocType as used in Documents structure in OfflineResponse
490 // DeviceNameSpacesBytes
491 // ]
492 //
493 // DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
494 //
495 // DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
496 //
497 // which is easily calculated below
498 //
499 size_t calculatedSize = 0;
500 calculatedSize += 1; // Array of size 4
501 calculatedSize += 1; // "DeviceAuthentication" less than 24 bytes
502 calculatedSize += sizeof("DeviceAuthentication") - 1; // Don't include trailing NUL
503 calculatedSize += sessionTranscriptSize; // Already CBOR encoded
504 size_t docTypeLen = eicStrLen(docType);
505 calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLen) + docTypeLen;
506 calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
507 calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
508 calculatedSize += expectedDeviceNamespacesSize;
509
510 // However note that we're authenticating DeviceAuthenticationBytes which
511 // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
512 // that in front.
513 size_t dabCalculatedSize = 0;
514 dabCalculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
515 dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
516 dabCalculatedSize += calculatedSize;
517
518 // Begin the bytestring for DeviceAuthenticationBytes;
519 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
520
521 eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
522
523 // Begins the bytestring for DeviceAuthentication;
524 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
525
526 eicCborAppendArray(&ctx->cbor, 4);
527 eicCborAppendString(&ctx->cbor, "DeviceAuthentication");
528 eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
529 eicCborAppendString(&ctx->cbor, docType);
530
531 // For the payload, the _encoded_ form follows here. We handle this by simply
532 // opening a bstr, and then writing the CBOR. This requires us to know the
533 // size of said bstr, ahead of time.
534 eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
535 eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedDeviceNamespacesSize);
536 ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
537
538 eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
539 return true;
540}
541
542bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
543 // HAL may use this object multiple times to retrieve data so need to reset various
544 // state objects here.
545 ctx->requestMessageValidated = false;
546 ctx->buildCbor = false;
547 ctx->accessControlProfileMaskValidated = 0;
548 ctx->accessControlProfileMaskUsesReaderAuth = 0;
549 ctx->accessControlProfileMaskFailedReaderAuth = 0;
550 ctx->accessControlProfileMaskFailedUserAuth = 0;
551 ctx->readerPublicKeySize = 0;
552 return true;
553}
554
555EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
556 EicPresentation* ctx, const char* nameSpace, const char* name,
557 unsigned int newNamespaceNumEntries, int32_t /* entrySize */,
558 const int* accessControlProfileIds, size_t numAccessControlProfileIds,
559 uint8_t* scratchSpace, size_t scratchSpaceSize) {
560 uint8_t* additionalDataCbor = scratchSpace;
561 const size_t additionalDataCborBufSize = scratchSpaceSize;
562 size_t additionalDataCborSize;
563
564 if (newNamespaceNumEntries > 0) {
565 eicCborAppendString(&ctx->cbor, nameSpace);
566 eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
567 }
568
569 // We'll need to calc and store a digest of additionalData to check that it's the same
570 // additionalData being passed in for every eicPresentationRetrieveEntryValue() call...
571 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
572 nameSpace, name, additionalDataCbor,
573 additionalDataCborBufSize, &additionalDataCborSize,
574 ctx->additionalDataSha256)) {
575 return EIC_ACCESS_CHECK_RESULT_FAILED;
576 }
577
578 if (numAccessControlProfileIds == 0) {
579 return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
580 }
581
582 // Access is granted if at least one of the profiles grants access.
583 //
584 // If an item is configured without any profiles, access is denied.
585 //
586 EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
587 for (size_t n = 0; n < numAccessControlProfileIds; n++) {
588 int id = accessControlProfileIds[n];
589 uint32_t idBitMask = (1 << id);
590
591 // If the access control profile wasn't validated, this is an error and we
592 // fail immediately.
593 bool validated = ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
594 if (!validated) {
595 eicDebug("No ACP for profile id %d", id);
596 return EIC_ACCESS_CHECK_RESULT_FAILED;
597 }
598
599 // Otherwise, we _did_ validate the profile. If none of the checks
600 // failed, we're done
601 bool failedUserAuth = ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
602 bool failedReaderAuth = ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
603 if (!failedUserAuth && !failedReaderAuth) {
604 result = EIC_ACCESS_CHECK_RESULT_OK;
605 break;
606 }
607 // One of the checks failed, convey which one
608 if (failedUserAuth) {
609 result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
610 } else {
611 result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
612 }
613 }
614 eicDebug("Result %d for name %s", result, name);
615
616 if (result == EIC_ACCESS_CHECK_RESULT_OK) {
617 eicCborAppendString(&ctx->cbor, name);
618 }
619 return result;
620}
621
622// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
623bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
624 size_t encryptedContentSize, uint8_t* content,
625 const char* nameSpace, const char* name,
626 const int* accessControlProfileIds,
627 size_t numAccessControlProfileIds, uint8_t* scratchSpace,
628 size_t scratchSpaceSize) {
629 uint8_t* additionalDataCbor = scratchSpace;
630 const size_t additionalDataCborBufSize = scratchSpaceSize;
631 size_t additionalDataCborSize;
632
633 uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
634 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
635 nameSpace, name, additionalDataCbor,
636 additionalDataCborBufSize, &additionalDataCborSize,
637 calculatedSha256)) {
638 return false;
639 }
640 if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
641 eicDebug("SHA-256 mismatch of additionalData");
642 return false;
643 }
644
645 if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent, encryptedContentSize,
646 additionalDataCbor, additionalDataCborSize, content)) {
647 eicDebug("Error decrypting content");
648 return false;
649 }
650
651 eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
652
653 return true;
654}
655
656bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
657 size_t* digestToBeMacedSize) {
658 if (!ctx->buildCbor) {
659 *digestToBeMacedSize = 0;
660 return true;
661 }
662 if (*digestToBeMacedSize != 32) {
663 return false;
664 }
665
666 // This verifies that the correct expectedDeviceNamespacesSize value was
667 // passed in at eicPresentationCalcMacKey() time.
668 if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
669 eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
670 return false;
671 }
672 eicCborFinal(&ctx->cbor, digestToBeMaced);
673 return true;
674}
675
676bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
677 size_t proofOfDeletionCborSize,
678 uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
679 EicCbor cbor;
680
681 eicCborInit(&cbor, NULL, 0);
682
683 // What we're going to sign is the COSE ToBeSigned structure which
684 // looks like the following:
685 //
686 // Sig_structure = [
687 // context : "Signature" / "Signature1" / "CounterSignature",
688 // body_protected : empty_or_serialized_map,
689 // ? sign_protected : empty_or_serialized_map,
690 // external_aad : bstr,
691 // payload : bstr
692 // ]
693 //
694 eicCborAppendArray(&cbor, 4);
695 eicCborAppendString(&cbor, "Signature1");
696
697 // The COSE Encoded protected headers is just a single field with
698 // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
699 // hard-code the CBOR encoding:
700 static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
701 eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
702 sizeof(coseEncodedProtectedHeaders));
703
704 // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
705 // so external_aad is the empty bstr
706 static const uint8_t externalAad[0] = {};
707 eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
708
709 // For the payload, the _encoded_ form follows here. We handle this by simply
710 // opening a bstr, and then writing the CBOR. This requires us to know the
711 // size of said bstr, ahead of time.
712 eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
713
714 // Finally, the CBOR that we're actually signing.
715 eicCborAppendArray(&cbor, 3);
716 eicCborAppendString(&cbor, "ProofOfDeletion");
717 eicCborAppendString(&cbor, docType);
718 eicCborAppendBool(&cbor, ctx->testCredential);
719
720 uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
721 eicCborFinal(&cbor, cborSha256);
722 if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
723 eicDebug("Error signing proofOfDeletion");
724 return false;
725 }
726
727 return true;
728}