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