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