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