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