blob: 104a5596978879458030bb6a22d6b100fe070bb7 [file] [log] [blame]
David Zeuthen630de2a2020-05-11 14:04:54 -04001/*
2 * Copyright 2020, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "EicPresentation.h"
Joseph Jangdabb3c52021-09-01 16:50:09 +080018#include "EicCommon.h"
David Zeuthen1eb12b22021-09-11 13:59:43 -040019#include "EicSession.h"
David Zeuthen630de2a2020-05-11 14:04:54 -040020
21#include <inttypes.h>
22
David Zeuthen1eb12b22021-09-11 13:59:43 -040023// Global used for assigning ids for presentation objects.
24//
25static uint32_t gPresentationLastIdAssigned = 0;
26
27bool eicPresentationInit(EicPresentation* ctx, uint32_t sessionId, bool testCredential,
28 const char* docType, size_t docTypeLength,
29 const uint8_t* encryptedCredentialKeys,
David Zeuthen49f2d252020-10-16 11:27:24 -040030 size_t encryptedCredentialKeysSize) {
Joseph Jangdabb3c52021-09-01 16:50:09 +080031 uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
David Zeuthen49f2d252020-10-16 11:27:24 -040032 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 //
Joseph Jangdabb3c52021-09-01 16:50:09 +080038 if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) {
David Zeuthen49f2d252020-10-16 11:27:24 -040039 /* do nothing */
Joseph Jangdabb3c52021-09-01 16:50:09 +080040 } else if (encryptedCredentialKeysSize == EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) {
David Zeuthen49f2d252020-10-16 11:27:24 -040041 expectPopSha256 = true;
42 } else {
43 eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
44 return false;
45 }
David Zeuthen630de2a2020-05-11 14:04:54 -040046
47 eicMemSet(ctx, '\0', sizeof(EicPresentation));
David Zeuthen1eb12b22021-09-11 13:59:43 -040048 ctx->sessionId = sessionId;
49
50 if (!eicNextId(&gPresentationLastIdAssigned)) {
51 eicDebug("Error getting id for object");
52 return false;
53 }
54 ctx->id = gPresentationLastIdAssigned;
David Zeuthen630de2a2020-05-11 14:04:54 -040055
56 if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
David Zeuthen49f2d252020-10-16 11:27:24 -040057 encryptedCredentialKeysSize,
David Zeuthen630de2a2020-05-11 14:04:54 -040058 // DocType is the additionalAuthenticatedData
Joseph Jangdabb3c52021-09-01 16:50:09 +080059 (const uint8_t*)docType, docTypeLength, credentialKeys)) {
David Zeuthen630de2a2020-05-11 14:04:54 -040060 eicDebug("Error decrypting CredentialKeys");
61 return false;
62 }
63
64 // It's supposed to look like this;
65 //
David Zeuthen49f2d252020-10-16 11:27:24 -040066 // Feature version 202009:
67 //
David Zeuthen630de2a2020-05-11 14:04:54 -040068 // CredentialKeys = [
69 // bstr, ; storageKey, a 128-bit AES key
David Zeuthen49f2d252020-10-16 11:27:24 -040070 // bstr, ; credentialPrivKey, the private key for credentialKey
David Zeuthen630de2a2020-05-11 14:04:54 -040071 // ]
72 //
David Zeuthen49f2d252020-10-16 11:27:24 -040073 // Feature version 202101:
David Zeuthen630de2a2020-05-11 14:04:54 -040074 //
David Zeuthen49f2d252020-10-16 11:27:24 -040075 // CredentialKeys = [
76 // bstr, ; storageKey, a 128-bit AES key
77 // bstr, ; credentialPrivKey, the private key for credentialKey
78 // bstr ; proofOfProvisioning SHA-256
79 // ]
David Zeuthen630de2a2020-05-11 14:04:54 -040080 //
David Zeuthen49f2d252020-10-16 11:27:24 -040081 // 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
David Zeuthen630de2a2020-05-11 14:04:54 -040087 eicDebug("Invalid CBOR for CredentialKeys");
88 return false;
89 }
David Zeuthen49f2d252020-10-16 11:27:24 -040090 if (expectPopSha256) {
91 if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) { // 32-byte bstr
92 eicDebug("Invalid CBOR for CredentialKeys");
93 return false;
94 }
95 }
David Zeuthen630de2a2020-05-11 14:04:54 -040096 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;
David Zeuthen49f2d252020-10-16 11:27:24 -040099 if (expectPopSha256) {
100 eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE);
101 }
David Zeuthen1eb12b22021-09-11 13:59:43 -0400102
103 eicDebug("Initialized presentation with id %" PRIu32, ctx->id);
104 return true;
105}
106
107bool 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
117bool eicPresentationGetId(EicPresentation* ctx, uint32_t* outId) {
118 *outId = ctx->id;
David Zeuthen630de2a2020-05-11 14:04:54 -0400119 return true;
120}
121
Joseph Jangdabb3c52021-09-01 16:50:09 +0800122bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType,
123 size_t docTypeLength, time_t now,
David Zeuthen630de2a2020-05-11 14:04:54 -0400124 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];
David Zeuthen49f2d252020-10-16 11:27:24 -0400128 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800149 eicCborAppendStringZ(&cbor, "ProofOfBinding");
David Zeuthen49f2d252020-10-16 11:27:24 -0400150 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;
David Zeuthen630de2a2020-05-11 14:04:54 -0400157
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
David Zeuthen49f2d252020-10-16 11:27:24 -0400169 validityNotBefore, validityNotAfter, proofOfBinding, proofOfBindingSize,
170 publicKeyCert, publicKeyCertSize)) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400171 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
Joseph Jangdabb3c52021-09-01 16:50:09 +0800182 (const uint8_t*)docType, docTypeLength, signingKeyBlob)) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400183 eicDebug("Error encrypting signing key");
184 return false;
185 }
186
187 return true;
188}
189
190bool 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
201bool 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 }
David Zeuthen1eb12b22021-09-11 13:59:43 -0400207 } while (ctx->authChallenge == EIC_KM_AUTH_CHALLENGE_UNSET);
David Zeuthen630de2a2020-05-11 14:04:54 -0400208 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
217bool 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) {
David Zeuthen1eb12b22021-09-11 13:59:43 -0400223 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
David Zeuthen630de2a2020-05-11 14:04:54 -0400241 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800272 eicCborAppendStringZ(&cbor, "Signature1");
David Zeuthen630de2a2020-05-11 14:04:54 -0400273
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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800330 eicCborAppendStringZ(&cbor, "ReaderAuthentication");
David Zeuthen630de2a2020-05-11 14:04:54 -0400331 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.
354bool 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
David Zeuthen1eb12b22021-09-11 13:59:43 -0400381static 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
David Zeuthen630de2a2020-05-11 14:04:54 -0400395bool 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) {
David Zeuthen1eb12b22021-09-11 13:59:43 -0400403 uint64_t authChallenge;
404 if (!getChallenge(ctx, &authChallenge)) {
405 return false;
406 }
407
David Zeuthenc6c950b2021-03-04 16:39:42 -0500408 // It doesn't make sense to accept any tokens if eicPresentationCreateAuthChallenge()
409 // was never called.
David Zeuthen1eb12b22021-09-11 13:59:43 -0400410 if (authChallenge == EIC_KM_AUTH_CHALLENGE_UNSET) {
411 eicDebug("Trying to validate tokens when no auth-challenge was previously generated");
David Zeuthenc6c950b2021-03-04 16:39:42 -0500412 return false;
413 }
414 // At least the verification-token must have the same challenge as what was generated.
David Zeuthen1eb12b22021-09-11 13:59:43 -0400415 if (verificationTokenChallenge != authChallenge) {
David Zeuthenc6c950b2021-03-04 16:39:42 -0500416 eicDebug("Challenge in verification token does not match the challenge "
417 "previously generated");
418 return false;
419 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400420 if (!eicOpsValidateAuthToken(
421 challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac,
422 macSize, verificationTokenChallenge, verificationTokenTimestamp,
423 verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) {
David Zeuthen1eb12b22021-09-11 13:59:43 -0400424 eicDebug("Error validating authToken");
David Zeuthen630de2a2020-05-11 14:04:54 -0400425 return false;
426 }
427 ctx->authTokenChallenge = challenge;
428 ctx->authTokenSecureUserId = secureUserId;
429 ctx->authTokenTimestamp = timeStamp;
430 ctx->verificationTokenTimestamp = verificationTokenTimestamp;
431 return true;
432}
433
434static 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
David Zeuthenc6c950b2021-03-04 16:39:42 -0500445 // Only ACP with auth-on-every-presentation - those with timeout == 0 - need the
446 // challenge to match...
David Zeuthen630de2a2020-05-11 14:04:54 -0400447 if (timeoutMillis == 0) {
David Zeuthen1eb12b22021-09-11 13:59:43 -0400448 uint64_t authChallenge;
449 if (!getChallenge(ctx, &authChallenge)) {
450 return false;
451 }
452
453 if (ctx->authTokenChallenge != authChallenge) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400454 eicDebug("Challenge in authToken (%" PRIu64
455 ") doesn't match the challenge "
456 "that was created (%" PRIu64 ") for this session",
David Zeuthen1eb12b22021-09-11 13:59:43 -0400457 ctx->authTokenChallenge, authChallenge);
David Zeuthen630de2a2020-05-11 14:04:54 -0400458 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
478static 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//
511bool 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],
Joseph Jangdabb3c52021-09-01 16:50:09 +0800516 bool* accessGranted,
517 uint8_t* scratchSpace,
518 size_t scratchSpaceSize) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400519 *accessGranted = false;
David Zeuthen630de2a2020-05-11 14:04:54 -0400520 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
David Zeuthen630de2a2020-05-11 14:04:54 -0400526 EicCbor cborBuilder;
Joseph Jangdabb3c52021-09-01 16:50:09 +0800527 eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
David Zeuthen630de2a2020-05-11 14:04:54 -0400528 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
Joseph Jangdabb3c52021-09-01 16:50:09 +0800542 ctx->accessControlProfileMaskValidated |= (1U << id);
David Zeuthen630de2a2020-05-11 14:04:54 -0400543 if (readerCertificateSize > 0) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800544 ctx->accessControlProfileMaskUsesReaderAuth |= (1U << id);
David Zeuthen630de2a2020-05-11 14:04:54 -0400545 }
546 if (!passedReaderAuth) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800547 ctx->accessControlProfileMaskFailedReaderAuth |= (1U << id);
David Zeuthen630de2a2020-05-11 14:04:54 -0400548 }
549 if (!passedUserAuth) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800550 ctx->accessControlProfileMaskFailedUserAuth |= (1U << id);
David Zeuthen630de2a2020-05-11 14:04:54 -0400551 }
552
553 if (passedUserAuth && passedReaderAuth) {
554 *accessGranted = true;
555 eicDebug("Access granted for id %d", id);
556 }
557 return true;
558}
559
560bool 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,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800564 size_t docTypeLength, unsigned int numNamespacesWithValues,
David Zeuthen630de2a2020-05-11 14:04:54 -0400565 size_t expectedDeviceNamespacesSize) {
David Zeuthen1eb12b22021-09-11 13:59:43 -0400566 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
David Zeuthen630de2a2020-05-11 14:04:54 -0400585 uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
586 if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800587 docTypeLength, signingKeyPriv)) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400588 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800627 eicCborAppendStringZ(&ctx->cbor, "MAC0");
David Zeuthen630de2a2020-05-11 14:04:54 -0400628
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
Joseph Jangdabb3c52021-09-01 16:50:09 +0800663 calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLength) + docTypeLength;
David Zeuthen630de2a2020-05-11 14:04:54 -0400664 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800685 eicCborAppendStringZ(&ctx->cbor, "DeviceAuthentication");
David Zeuthen630de2a2020-05-11 14:04:54 -0400686 eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800687 eicCborAppendString(&ctx->cbor, docType, docTypeLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400688
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
700bool 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
713EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
Joseph Jangdabb3c52021-09-01 16:50:09 +0800714 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,
David Zeuthen630de2a2020-05-11 14:04:54 -0400718 uint8_t* scratchSpace, size_t scratchSpaceSize) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800719 (void)entrySize;
David Zeuthen630de2a2020-05-11 14:04:54 -0400720 uint8_t* additionalDataCbor = scratchSpace;
Joseph Jangdabb3c52021-09-01 16:50:09 +0800721 size_t additionalDataCborBufferSize = scratchSpaceSize;
David Zeuthen630de2a2020-05-11 14:04:54 -0400722 size_t additionalDataCborSize;
723
724 if (newNamespaceNumEntries > 0) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800725 eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
David Zeuthen630de2a2020-05-11 14:04:54 -0400726 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...
David Zeuthenaf7e9cf2021-06-10 18:34:24 -0400731 //
732 ctx->accessCheckOk = false;
David Zeuthen630de2a2020-05-11 14:04:54 -0400733 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800734 nameSpace, nameSpaceLength, name, nameLength,
735 additionalDataCbor, additionalDataCborBufferSize,
736 &additionalDataCborSize,
David Zeuthen630de2a2020-05-11 14:04:54 -0400737 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) {
Joseph Jangdabb3c52021-09-01 16:50:09 +0800780 eicCborAppendString(&ctx->cbor, name, nameLength);
David Zeuthenaf7e9cf2021-06-10 18:34:24 -0400781 ctx->accessCheckOk = true;
David Zeuthen630de2a2020-05-11 14:04:54 -0400782 }
783 return result;
784}
785
786// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
787bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
788 size_t encryptedContentSize, uint8_t* content,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800789 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,
David Zeuthen630de2a2020-05-11 14:04:54 -0400794 size_t scratchSpaceSize) {
795 uint8_t* additionalDataCbor = scratchSpace;
Joseph Jangdabb3c52021-09-01 16:50:09 +0800796 size_t additionalDataCborBufferSize = scratchSpaceSize;
David Zeuthen630de2a2020-05-11 14:04:54 -0400797 size_t additionalDataCborSize;
798
799 uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
800 if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800801 nameSpace, nameSpaceLength, name, nameLength,
802 additionalDataCbor, additionalDataCborBufferSize,
803 &additionalDataCborSize,
David Zeuthen630de2a2020-05-11 14:04:54 -0400804 calculatedSha256)) {
805 return false;
806 }
David Zeuthenaf7e9cf2021-06-10 18:34:24 -0400807
David Zeuthen630de2a2020-05-11 14:04:54 -0400808 if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
809 eicDebug("SHA-256 mismatch of additionalData");
810 return false;
811 }
David Zeuthenaf7e9cf2021-06-10 18:34:24 -0400812 if (!ctx->accessCheckOk) {
813 eicDebug("Attempting to retrieve a value for which access is not granted");
814 return false;
815 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400816
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
828bool 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
Joseph Jangdabb3c52021-09-01 16:50:09 +0800848bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, size_t docTypeLength,
David Zeuthen49f2d252020-10-16 11:27:24 -0400849 const uint8_t* challenge, size_t challengeSize,
Joseph Jangdabb3c52021-09-01 16:50:09 +0800850 bool includeChallenge,
851 size_t proofOfDeletionCborSize,
David Zeuthen630de2a2020-05-11 14:04:54 -0400852 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800869 eicCborAppendStringZ(&cbor, "Signature1");
David Zeuthen630de2a2020-05-11 14:04:54 -0400870
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.
David Zeuthen49f2d252020-10-16 11:27:24 -0400889 eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800890 eicCborAppendStringZ(&cbor, "ProofOfDeletion");
891 eicCborAppendString(&cbor, docType, docTypeLength);
David Zeuthen49f2d252020-10-16 11:27:24 -0400892 if (includeChallenge) {
893 eicCborAppendByteString(&cbor, challenge, challengeSize);
894 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400895 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}
David Zeuthen49f2d252020-10-16 11:27:24 -0400906
Joseph Jangdabb3c52021-09-01 16:50:09 +0800907bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType,
908 size_t docTypeLength, bool testCredential,
David Zeuthen49f2d252020-10-16 11:27:24 -0400909 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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800928 eicCborAppendStringZ(&cbor, "Signature1");
David Zeuthen49f2d252020-10-16 11:27:24 -0400929
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);
Joseph Jangdabb3c52021-09-01 16:50:09 +0800949 eicCborAppendStringZ(&cbor, "ProofOfOwnership");
950 eicCborAppendString(&cbor, docType, docTypeLength);
David Zeuthen49f2d252020-10-16 11:27:24 -0400951 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}