• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
eicPresentationCalcMacKey(EicPresentation * ctx,const uint8_t * sessionTranscript,size_t sessionTranscriptSize,const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],const uint8_t signingKeyBlob[60],const char * docType,size_t docTypeLength,unsigned int numNamespacesWithValues,size_t expectedDeviceNamespacesSize)560 bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
561                                size_t sessionTranscriptSize,
562                                const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
563                                const uint8_t signingKeyBlob[60], const char* docType,
564                                size_t docTypeLength, unsigned int numNamespacesWithValues,
565                                size_t expectedDeviceNamespacesSize) {
566     if (ctx->sessionId != 0) {
567         EicSession* session = eicSessionGetForId(ctx->sessionId);
568         if (session == NULL) {
569             eicDebug("Error looking up session for sessionId %" PRIu32, ctx->sessionId);
570             return false;
571         }
572         EicSha256Ctx sha256;
573         uint8_t sessionTranscriptSha256[EIC_SHA256_DIGEST_SIZE];
574         eicOpsSha256Init(&sha256);
575         eicOpsSha256Update(&sha256, sessionTranscript, sessionTranscriptSize);
576         eicOpsSha256Final(&sha256, sessionTranscriptSha256);
577         if (eicCryptoMemCmp(sessionTranscriptSha256, session->sessionTranscriptSha256,
578                             EIC_SHA256_DIGEST_SIZE) != 0) {
579             eicDebug("SessionTranscript mismatch");
580             return false;
581         }
582         readerEphemeralPublicKey = session->readerEphemeralPublicKey;
583     }
584 
585     uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
586     if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
587                                 docTypeLength, signingKeyPriv)) {
588         eicDebug("Error decrypting signingKeyBlob");
589         return false;
590     }
591 
592     uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
593     if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
594         eicDebug("ECDH failed");
595         return false;
596     }
597 
598     EicCbor cbor;
599     eicCborInit(&cbor, NULL, 0);
600     eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
601     eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
602     uint8_t salt[EIC_SHA256_DIGEST_SIZE];
603     eicCborFinal(&cbor, salt);
604 
605     const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
606     uint8_t derivedKey[32];
607     if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt), info, sizeof(info),
608                     derivedKey, sizeof(derivedKey))) {
609         eicDebug("HKDF failed");
610         return false;
611     }
612 
613     eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
614     ctx->buildCbor = true;
615 
616     // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
617     // structure which looks like the following:
618     //
619     // MAC_structure = [
620     //   context : "MAC" / "MAC0",
621     //   protected : empty_or_serialized_map,
622     //   external_aad : bstr,
623     //   payload : bstr
624     // ]
625     //
626     eicCborAppendArray(&ctx->cbor, 4);
627     eicCborAppendStringZ(&ctx->cbor, "MAC0");
628 
629     // The COSE Encoded protected headers is just a single field with
630     // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
631     // hard-code the CBOR encoding:
632     static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
633     eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
634                             sizeof(coseEncodedProtectedHeaders));
635 
636     // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
637     // so external_aad is the empty bstr
638     static const uint8_t externalAad[0] = {};
639     eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
640 
641     // For the payload, the _encoded_ form follows here. We handle this by simply
642     // opening a bstr, and then writing the CBOR. This requires us to know the
643     // size of said bstr, ahead of time... the CBOR to be written is
644     //
645     //   DeviceAuthentication = [
646     //      "DeviceAuthentication",
647     //      SessionTranscript,
648     //      DocType,                ; DocType as used in Documents structure in OfflineResponse
649     //      DeviceNameSpacesBytes
650     //   ]
651     //
652     //   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
653     //
654     //   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
655     //
656     // which is easily calculated below
657     //
658     size_t calculatedSize = 0;
659     calculatedSize += 1;  // Array of size 4
660     calculatedSize += 1;  // "DeviceAuthentication" less than 24 bytes
661     calculatedSize += sizeof("DeviceAuthentication") - 1;  // Don't include trailing NUL
662     calculatedSize += sessionTranscriptSize;               // Already CBOR encoded
663     calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLength) + docTypeLength;
664     calculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
665     calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
666     calculatedSize += expectedDeviceNamespacesSize;
667 
668     // However note that we're authenticating DeviceAuthenticationBytes which
669     // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
670     // that in front.
671     size_t dabCalculatedSize = 0;
672     dabCalculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
673     dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
674     dabCalculatedSize += calculatedSize;
675 
676     // Begin the bytestring for DeviceAuthenticationBytes;
677     eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
678 
679     eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
680 
681     // Begins the bytestring for DeviceAuthentication;
682     eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
683 
684     eicCborAppendArray(&ctx->cbor, 4);
685     eicCborAppendStringZ(&ctx->cbor, "DeviceAuthentication");
686     eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
687     eicCborAppendString(&ctx->cbor, docType, docTypeLength);
688 
689     // For the payload, the _encoded_ form follows here. We handle this by simply
690     // opening a bstr, and then writing the CBOR. This requires us to know the
691     // size of said bstr, ahead of time.
692     eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
693     eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedDeviceNamespacesSize);
694     ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
695 
696     eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
697     return true;
698 }
699 
eicPresentationStartRetrieveEntries(EicPresentation * ctx)700 bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
701     // HAL may use this object multiple times to retrieve data so need to reset various
702     // state objects here.
703     ctx->requestMessageValidated = false;
704     ctx->buildCbor = false;
705     ctx->accessControlProfileMaskValidated = 0;
706     ctx->accessControlProfileMaskUsesReaderAuth = 0;
707     ctx->accessControlProfileMaskFailedReaderAuth = 0;
708     ctx->accessControlProfileMaskFailedUserAuth = 0;
709     ctx->readerPublicKeySize = 0;
710     return true;
711 }
712 
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)713 EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
714         EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength,
715         const char* name, size_t nameLength,
716         unsigned int newNamespaceNumEntries, int32_t entrySize,
717         const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
718         uint8_t* scratchSpace, size_t scratchSpaceSize) {
719     (void)entrySize;
720     uint8_t* additionalDataCbor = scratchSpace;
721     size_t additionalDataCborBufferSize = scratchSpaceSize;
722     size_t additionalDataCborSize;
723 
724     if (newNamespaceNumEntries > 0) {
725         eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
726         eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
727     }
728 
729     // We'll need to calc and store a digest of additionalData to check that it's the same
730     // additionalData being passed in for every eicPresentationRetrieveEntryValue() call...
731     //
732     ctx->accessCheckOk = false;
733     if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
734                                         nameSpace, nameSpaceLength, name, nameLength,
735                                         additionalDataCbor, additionalDataCborBufferSize,
736                                         &additionalDataCborSize,
737                                         ctx->additionalDataSha256)) {
738         return EIC_ACCESS_CHECK_RESULT_FAILED;
739     }
740 
741     if (numAccessControlProfileIds == 0) {
742         return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
743     }
744 
745     // Access is granted if at least one of the profiles grants access.
746     //
747     // If an item is configured without any profiles, access is denied.
748     //
749     EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
750     for (size_t n = 0; n < numAccessControlProfileIds; n++) {
751         int id = accessControlProfileIds[n];
752         uint32_t idBitMask = (1 << id);
753 
754         // If the access control profile wasn't validated, this is an error and we
755         // fail immediately.
756         bool validated = ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
757         if (!validated) {
758             eicDebug("No ACP for profile id %d", id);
759             return EIC_ACCESS_CHECK_RESULT_FAILED;
760         }
761 
762         // Otherwise, we _did_ validate the profile. If none of the checks
763         // failed, we're done
764         bool failedUserAuth = ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
765         bool failedReaderAuth = ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
766         if (!failedUserAuth && !failedReaderAuth) {
767             result = EIC_ACCESS_CHECK_RESULT_OK;
768             break;
769         }
770         // One of the checks failed, convey which one
771         if (failedUserAuth) {
772             result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
773         } else {
774             result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
775         }
776     }
777     eicDebug("Result %d for name %s", result, name);
778 
779     if (result == EIC_ACCESS_CHECK_RESULT_OK) {
780         eicCborAppendString(&ctx->cbor, name, nameLength);
781         ctx->accessCheckOk = true;
782     }
783     return result;
784 }
785 
786 // 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)787 bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
788                                        size_t encryptedContentSize, uint8_t* content,
789                                        const char* nameSpace, size_t nameSpaceLength,
790                                        const char* name, size_t nameLength,
791                                        const uint8_t* accessControlProfileIds,
792                                        size_t numAccessControlProfileIds,
793                                        uint8_t* scratchSpace,
794                                        size_t scratchSpaceSize) {
795     uint8_t* additionalDataCbor = scratchSpace;
796     size_t additionalDataCborBufferSize = scratchSpaceSize;
797     size_t additionalDataCborSize;
798 
799     uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
800     if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
801                                         nameSpace, nameSpaceLength, name, nameLength,
802                                         additionalDataCbor, additionalDataCborBufferSize,
803                                         &additionalDataCborSize,
804                                         calculatedSha256)) {
805         return false;
806     }
807 
808     if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
809         eicDebug("SHA-256 mismatch of additionalData");
810         return false;
811     }
812     if (!ctx->accessCheckOk) {
813         eicDebug("Attempting to retrieve a value for which access is not granted");
814         return false;
815     }
816 
817     if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent, encryptedContentSize,
818                                 additionalDataCbor, additionalDataCborSize, content)) {
819         eicDebug("Error decrypting content");
820         return false;
821     }
822 
823     eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
824 
825     return true;
826 }
827 
eicPresentationFinishRetrieval(EicPresentation * ctx,uint8_t * digestToBeMaced,size_t * digestToBeMacedSize)828 bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
829                                     size_t* digestToBeMacedSize) {
830     if (!ctx->buildCbor) {
831         *digestToBeMacedSize = 0;
832         return true;
833     }
834     if (*digestToBeMacedSize != 32) {
835         return false;
836     }
837 
838     // This verifies that the correct expectedDeviceNamespacesSize value was
839     // passed in at eicPresentationCalcMacKey() time.
840     if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
841         eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
842         return false;
843     }
844     eicCborFinal(&ctx->cbor, digestToBeMaced);
845     return true;
846 }
847 
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])848 bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, size_t docTypeLength,
849                                      const uint8_t* challenge, size_t challengeSize,
850                                      bool includeChallenge,
851                                      size_t proofOfDeletionCborSize,
852                                      uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
853     EicCbor cbor;
854 
855     eicCborInit(&cbor, NULL, 0);
856 
857     // What we're going to sign is the COSE ToBeSigned structure which
858     // looks like the following:
859     //
860     // Sig_structure = [
861     //   context : "Signature" / "Signature1" / "CounterSignature",
862     //   body_protected : empty_or_serialized_map,
863     //   ? sign_protected : empty_or_serialized_map,
864     //   external_aad : bstr,
865     //   payload : bstr
866     //  ]
867     //
868     eicCborAppendArray(&cbor, 4);
869     eicCborAppendStringZ(&cbor, "Signature1");
870 
871     // The COSE Encoded protected headers is just a single field with
872     // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
873     // hard-code the CBOR encoding:
874     static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
875     eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
876                             sizeof(coseEncodedProtectedHeaders));
877 
878     // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
879     // so external_aad is the empty bstr
880     static const uint8_t externalAad[0] = {};
881     eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
882 
883     // For the payload, the _encoded_ form follows here. We handle this by simply
884     // opening a bstr, and then writing the CBOR. This requires us to know the
885     // size of said bstr, ahead of time.
886     eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
887 
888     // Finally, the CBOR that we're actually signing.
889     eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
890     eicCborAppendStringZ(&cbor, "ProofOfDeletion");
891     eicCborAppendString(&cbor, docType, docTypeLength);
892     if (includeChallenge) {
893         eicCborAppendByteString(&cbor, challenge, challengeSize);
894     }
895     eicCborAppendBool(&cbor, ctx->testCredential);
896 
897     uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
898     eicCborFinal(&cbor, cborSha256);
899     if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
900         eicDebug("Error signing proofOfDeletion");
901         return false;
902     }
903 
904     return true;
905 }
906 
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])907 bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType,
908                                    size_t docTypeLength, bool testCredential,
909                                    const uint8_t* challenge, size_t challengeSize,
910                                    size_t proofOfOwnershipCborSize,
911                                    uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
912     EicCbor cbor;
913 
914     eicCborInit(&cbor, NULL, 0);
915 
916     // What we're going to sign is the COSE ToBeSigned structure which
917     // looks like the following:
918     //
919     // Sig_structure = [
920     //   context : "Signature" / "Signature1" / "CounterSignature",
921     //   body_protected : empty_or_serialized_map,
922     //   ? sign_protected : empty_or_serialized_map,
923     //   external_aad : bstr,
924     //   payload : bstr
925     //  ]
926     //
927     eicCborAppendArray(&cbor, 4);
928     eicCborAppendStringZ(&cbor, "Signature1");
929 
930     // The COSE Encoded protected headers is just a single field with
931     // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
932     // hard-code the CBOR encoding:
933     static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
934     eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
935                             sizeof(coseEncodedProtectedHeaders));
936 
937     // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
938     // so external_aad is the empty bstr
939     static const uint8_t externalAad[0] = {};
940     eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
941 
942     // For the payload, the _encoded_ form follows here. We handle this by simply
943     // opening a bstr, and then writing the CBOR. This requires us to know the
944     // size of said bstr, ahead of time.
945     eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfOwnershipCborSize);
946 
947     // Finally, the CBOR that we're actually signing.
948     eicCborAppendArray(&cbor, 4);
949     eicCborAppendStringZ(&cbor, "ProofOfOwnership");
950     eicCborAppendString(&cbor, docType, docTypeLength);
951     eicCborAppendByteString(&cbor, challenge, challengeSize);
952     eicCborAppendBool(&cbor, testCredential);
953 
954     uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
955     eicCborFinal(&cbor, cborSha256);
956     if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
957         eicDebug("Error signing proofOfDeletion");
958         return false;
959     }
960 
961     return true;
962 }
963