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