1 /*
2 * Copyright (C) 2021 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 #define TLOG_TAG "apploader-cose"
18
19 #include <apploader/cbor.h>
20 #include <apploader/cose.h>
21 #include <assert.h>
22 #include <inttypes.h>
23 #include <openssl/bn.h>
24 #include <openssl/crypto.h>
25 #include <openssl/ec.h>
26 #include <openssl/err.h>
27 #include <openssl/evp.h>
28 #include <openssl/rand.h>
29 #include <openssl/x509.h>
30 #include <stddef.h>
31 #include <trusty_log.h>
32 #include <array>
33 #include <optional>
34 #include <vector>
35
36 #ifdef __COSE_HOST__
37 #define COSE_PRINT_ERROR(...) \
38 if (!gSilenceErrors) { \
39 fprintf(stderr, __VA_ARGS__); \
40 }
41 #else
42 #define COSE_PRINT_ERROR(...) \
43 if (!gSilenceErrors) { \
44 TLOGE(__VA_ARGS__); \
45 }
46 #endif
47
48 #ifdef APPLOADER_PACKAGE_SIGN_P384
49 #define APPLOADER_DSA_LENGTH SHA384_DIGEST_LENGTH
50 #define APPLOADER_DSA_NID NID_secp384r1
51 #else
52 #define APPLOADER_DSA_LENGTH SHA256_DIGEST_LENGTH
53 #define APPLOADER_DSA_NID NID_X9_62_prime256v1
54 #endif
55
56 #ifdef APPLOADER_PACKAGE_CIPHER_A256
57 #define EVP_aes_trusty_gcm() EVP_aes_256_gcm()
58 #else
59 #define EVP_aes_trusty_gcm() EVP_aes_128_gcm()
60 #endif
61
62 static bool gSilenceErrors = false;
63
64 constexpr size_t kEcdsaValueSize = APPLOADER_DSA_LENGTH;
65 constexpr size_t kEcdsaSignatureSize = 2 * kEcdsaValueSize;
66
coseSetSilenceErrors(bool value)67 bool coseSetSilenceErrors(bool value) {
68 bool old = gSilenceErrors;
69 gSilenceErrors = value;
70 return old;
71 }
72
73 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, std::function<void(BIGNUM*)>>;
74 using EC_KEY_Ptr = std::unique_ptr<EC_KEY, std::function<void(EC_KEY*)>>;
75 using ECDSA_SIG_Ptr =
76 std::unique_ptr<ECDSA_SIG, std::function<void(ECDSA_SIG*)>>;
77 using EVP_CIPHER_CTX_Ptr =
78 std::unique_ptr<EVP_CIPHER_CTX, std::function<void(EVP_CIPHER_CTX*)>>;
79
80 using SHADigest = std::array<uint8_t, kEcdsaValueSize>;
81
coseBuildToBeSigned(const std::span<const uint8_t> & encodedProtectedHeaders,const std::vector<uint8_t> & data)82 static std::vector<uint8_t> coseBuildToBeSigned(
83 const std::span<const uint8_t>& encodedProtectedHeaders,
84 const std::vector<uint8_t>& data) {
85 cbor::VectorCborEncoder enc;
86 enc.encodeArray([&](auto& enc) {
87 enc.encodeTstr("Signature1");
88 enc.encodeBstr(encodedProtectedHeaders);
89 // We currently don't support Externally Supplied Data (RFC 8152
90 // section 4.3) so external_aad is the empty bstr
91 enc.encodeEmptyBstr();
92 enc.encodeBstr(data);
93 });
94
95 return enc.intoVec();
96 }
97
getRandom(size_t numBytes)98 static std::optional<std::vector<uint8_t>> getRandom(size_t numBytes) {
99 std::vector<uint8_t> output;
100 output.resize(numBytes);
101 if (RAND_bytes(output.data(), numBytes) != 1) {
102 COSE_PRINT_ERROR("RAND_bytes: failed getting %zu random\n", numBytes);
103 return {};
104 }
105 return output;
106 }
107
108 #ifdef APPLOADER_PACKAGE_SIGN_P384
sha(const std::vector<std::tuple<const void *,size_t>> & data_list)109 static SHADigest sha(
110 const std::vector<std::tuple<const void*, size_t>>& data_list) {
111 SHADigest ret;
112 SHA512_CTX ctx; // Note that SHA384 functions use a SHA512 context
113
114 SHA384_Init(&ctx);
115
116 for (auto data : data_list) {
117 SHA384_Update(&ctx, std::get<0>(data), std::get<1>(data));
118 }
119
120 SHA384_Final((unsigned char*)ret.data(), &ctx);
121
122 return ret;
123 }
124 #else
sha(const std::vector<std::tuple<const void *,size_t>> & data_list)125 static SHADigest sha(
126 const std::vector<std::tuple<const void*, size_t>>& data_list) {
127 SHADigest ret;
128 SHA256_CTX ctx;
129
130 SHA256_Init(&ctx);
131
132 for (auto data : data_list) {
133 SHA256_Update(&ctx, std::get<0>(data), std::get<1>(data));
134 }
135
136 SHA256_Final((unsigned char*)ret.data(), &ctx);
137
138 return ret;
139 }
140 #endif
141
sha(const std::vector<uint8_t> & data)142 static SHADigest sha(const std::vector<uint8_t>& data) {
143 return sha({{data.data(), data.size()}});
144 }
145
signEcDsaDigest(const std::vector<uint8_t> & key,const SHADigest & dataDigest)146 static std::optional<std::vector<uint8_t>> signEcDsaDigest(
147 const std::vector<uint8_t>& key,
148 const SHADigest& dataDigest) {
149 const unsigned char* k = key.data();
150 auto ecKey =
151 EC_KEY_Ptr(d2i_ECPrivateKey(nullptr, &k, key.size()), EC_KEY_free);
152 if (!ecKey) {
153 COSE_PRINT_ERROR("Error parsing EC private key\n");
154 return {};
155 }
156
157 if (EC_KEY_check_key(ecKey.get()) == 0) {
158 COSE_PRINT_ERROR("Error checking EC private key\n");
159 return {};
160 }
161
162 const EC_GROUP* ecGroup = EC_KEY_get0_group(ecKey.get());
163 if (EC_GROUP_get_curve_name(ecGroup) != APPLOADER_DSA_NID) {
164 COSE_PRINT_ERROR("Error checking EC group (not secp384r1)\n");
165 return {};
166 }
167
168 auto sig = ECDSA_SIG_Ptr(
169 ECDSA_do_sign(dataDigest.data(), dataDigest.size(), ecKey.get()),
170 ECDSA_SIG_free);
171 if (!sig) {
172 COSE_PRINT_ERROR("Error signing digest:\n");
173 return {};
174 }
175 size_t len = i2d_ECDSA_SIG(sig.get(), nullptr);
176 std::vector<uint8_t> signature;
177 signature.resize(len);
178 unsigned char* p = (unsigned char*)signature.data();
179 i2d_ECDSA_SIG(sig.get(), &p);
180 return signature;
181 }
182
signEcDsa(const std::vector<uint8_t> & key,const std::vector<uint8_t> & data)183 static std::optional<std::vector<uint8_t>> signEcDsa(
184 const std::vector<uint8_t>& key,
185 const std::vector<uint8_t>& data) {
186 return signEcDsaDigest(key, sha(data));
187 }
188
ecdsaSignatureDerToCose(const std::vector<uint8_t> & ecdsaDerSignature,std::vector<uint8_t> & ecdsaCoseSignature)189 static bool ecdsaSignatureDerToCose(
190 const std::vector<uint8_t>& ecdsaDerSignature,
191 std::vector<uint8_t>& ecdsaCoseSignature) {
192 const unsigned char* p = ecdsaDerSignature.data();
193 auto sig =
194 ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, ecdsaDerSignature.size()),
195 ECDSA_SIG_free);
196 if (!sig) {
197 COSE_PRINT_ERROR("Error decoding DER signature\n");
198 return false;
199 }
200
201 const BIGNUM* rBn;
202 const BIGNUM* sBn;
203 ECDSA_SIG_get0(sig.get(), &rBn, &sBn);
204
205 /*
206 * Older versions of OpenSSL also do not have BN_bn2binpad,
207 * so we need to use BN_bn2bin with the correct offsets.
208 * Each of the output values is a 32-byte big-endian number,
209 * while the inputs are BIGNUMs stored in host format.
210 * We can insert the padding ourselves by zeroing the output array,
211 * then placing the output of BN_bn2bin so its end aligns
212 * with the end of the 32-byte big-endian number.
213 */
214 auto rBnSize = BN_num_bytes(rBn);
215 if (rBnSize < 0 || static_cast<size_t>(rBnSize) > kEcdsaValueSize) {
216 COSE_PRINT_ERROR("Invalid ECDSA r value size (%d)\n", rBnSize);
217 return false;
218 }
219 auto sBnSize = BN_num_bytes(sBn);
220 if (sBnSize < 0 || static_cast<size_t>(sBnSize) > kEcdsaValueSize) {
221 COSE_PRINT_ERROR("Invalid ECDSA s value size (%d)\n", sBnSize);
222 return false;
223 }
224
225 ecdsaCoseSignature.clear();
226 ecdsaCoseSignature.resize(kEcdsaSignatureSize, 0);
227 if (BN_bn2bin(rBn, ecdsaCoseSignature.data() + kEcdsaValueSize - rBnSize) !=
228 rBnSize) {
229 COSE_PRINT_ERROR("Error encoding r\n");
230 return false;
231 }
232 if (BN_bn2bin(sBn, ecdsaCoseSignature.data() + kEcdsaSignatureSize -
233 sBnSize) != sBnSize) {
234 COSE_PRINT_ERROR("Error encoding s\n");
235 return false;
236 }
237 return true;
238 }
239
coseSignEcDsa(const std::vector<uint8_t> & key,uint8_t keyId,const std::vector<uint8_t> & data,const std::span<const uint8_t> & encodedProtectedHeaders,std::span<const uint8_t> & unprotectedHeaders,bool detachContent,bool tagged)240 std::optional<std::vector<uint8_t>> coseSignEcDsa(
241 const std::vector<uint8_t>& key,
242 uint8_t keyId,
243 const std::vector<uint8_t>& data,
244 const std::span<const uint8_t>& encodedProtectedHeaders,
245 std::span<const uint8_t>& unprotectedHeaders,
246 bool detachContent,
247 bool tagged) {
248 cbor::VectorCborEncoder addnHeadersEnc;
249 addnHeadersEnc.encodeMap([&](auto& enc) {
250 enc.encodeKeyValue(COSE_LABEL_KID, [&](auto& enc) {
251 enc.encodeBstr(std::span(&keyId, 1));
252 });
253 });
254 auto updatedUnprotectedHeaders =
255 cbor::mergeMaps(unprotectedHeaders, addnHeadersEnc.view());
256 if (!updatedUnprotectedHeaders.has_value()) {
257 COSE_PRINT_ERROR("Error updating unprotected headers\n");
258 return {};
259 }
260
261 std::vector<uint8_t> toBeSigned =
262 coseBuildToBeSigned(encodedProtectedHeaders, data);
263
264 std::optional<std::vector<uint8_t>> derSignature =
265 signEcDsa(key, toBeSigned);
266 if (!derSignature) {
267 COSE_PRINT_ERROR("Error signing toBeSigned data\n");
268 return {};
269 }
270 std::vector<uint8_t> coseSignature;
271 if (!ecdsaSignatureDerToCose(derSignature.value(), coseSignature)) {
272 COSE_PRINT_ERROR(
273 "Error converting ECDSA signature from DER to COSE format\n");
274 return {};
275 }
276
277 auto arrayEncodingFn = [&](auto& enc) {
278 enc.encodeArray([&](auto& enc) {
279 /* 1: protected:empty_or_serialized_map */
280 enc.encodeBstr(encodedProtectedHeaders);
281
282 /* 2: unprotected:map */
283 enc.copyBytes(updatedUnprotectedHeaders.value());
284
285 /* 3: payload:bstr_or_nil */
286 if (detachContent) {
287 enc.encodeNull();
288 } else {
289 enc.encodeBstr(data);
290 }
291
292 /* 4: signature:bstr */
293 enc.encodeBstr(coseSignature);
294 });
295 };
296
297 cbor::VectorCborEncoder enc;
298 if (tagged) {
299 enc.encodeTag(COSE_TAG_SIGN1, arrayEncodingFn);
300 } else {
301 arrayEncodingFn(enc);
302 }
303
304 return enc.intoVec();
305 }
306
coseIsSigned(CoseByteView data,size_t * signatureLength)307 bool coseIsSigned(CoseByteView data, size_t* signatureLength) {
308 struct CborIn in;
309 uint64_t tag;
310
311 CborInInit(data.data(), data.size(), &in);
312 while (!CborInAtEnd(&in)) {
313 if (CborReadTag(&in, &tag) == CBOR_READ_RESULT_OK) {
314 if (tag == COSE_TAG_SIGN1) {
315 if (signatureLength) {
316 /* read tag item to get its size */
317 CborReadSkip(&in);
318 *signatureLength = CborInOffset(&in);
319 }
320 return true;
321 }
322 } else if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
323 /*
324 * CborReadSkip uses a stack to track nested content so parsing can
325 * fail if nesting of CBOR items causes stack exhaustion. The COSE
326 * format does not cause stack exhaustion so the input must be bad.
327 */
328 return false;
329 }
330 }
331
332 return false;
333 }
334
checkEcDsaSignature(const SHADigest & digest,const uint8_t * signature,const uint8_t * publicKey,size_t publicKeySize)335 static bool checkEcDsaSignature(const SHADigest& digest,
336 const uint8_t* signature,
337 const uint8_t* publicKey,
338 size_t publicKeySize) {
339 auto rBn =
340 BIGNUM_Ptr(BN_bin2bn(signature, kEcdsaValueSize, nullptr), BN_free);
341 if (rBn.get() == nullptr) {
342 COSE_PRINT_ERROR("Error creating BIGNUM for r\n");
343 return false;
344 }
345
346 auto sBn = BIGNUM_Ptr(
347 BN_bin2bn(signature + kEcdsaValueSize, kEcdsaValueSize, nullptr),
348 BN_free);
349 if (sBn.get() == nullptr) {
350 COSE_PRINT_ERROR("Error creating BIGNUM for s\n");
351 return false;
352 }
353
354 auto sig = ECDSA_SIG_Ptr(ECDSA_SIG_new(), ECDSA_SIG_free);
355 if (!sig) {
356 COSE_PRINT_ERROR("Error allocating ECDSA_SIG\n");
357 return false;
358 }
359
360 ECDSA_SIG_set0(sig.get(), rBn.release(), sBn.release());
361
362 const unsigned char* k = publicKey;
363 auto ecKey =
364 EC_KEY_Ptr(d2i_EC_PUBKEY(nullptr, &k, publicKeySize), EC_KEY_free);
365 if (!ecKey) {
366 COSE_PRINT_ERROR("Error parsing EC public key\n");
367 return false;
368 }
369
370 int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(),
371 ecKey.get());
372 if (rc != 1) {
373 COSE_PRINT_ERROR("Error verifying signature (rc=%d)\n", rc);
374 return false;
375 }
376
377 return true;
378 }
379
coseCheckEcDsaSignature(const std::vector<uint8_t> & signatureCoseSign1,const std::vector<uint8_t> & detachedContent,const std::vector<uint8_t> & publicKey)380 bool coseCheckEcDsaSignature(const std::vector<uint8_t>& signatureCoseSign1,
381 const std::vector<uint8_t>& detachedContent,
382 const std::vector<uint8_t>& publicKey) {
383 struct CborIn in;
384 CborInInit(signatureCoseSign1.data(), signatureCoseSign1.size(), &in);
385
386 uint64_t tag;
387 /* COSE message tag is optional */
388 if (CborReadTag(&in, &tag) == CBOR_READ_RESULT_OK) {
389 if (tag != COSE_TAG_SIGN1) {
390 COSE_PRINT_ERROR("Passed-in COSE_Sign1 contained invalid tag\n");
391 return false;
392 }
393 }
394
395 size_t arraySize;
396 if (CborReadArray(&in, &arraySize) != CBOR_READ_RESULT_OK) {
397 COSE_PRINT_ERROR("Value for COSE_Sign1 is not an array\n");
398 return false;
399 }
400
401 if (arraySize != 4) {
402 COSE_PRINT_ERROR("Value for COSE_Sign1 is not an array of size 4\n");
403 return false;
404 }
405
406 const uint8_t* encodedProtectedHeadersPtr;
407 size_t encodedProtectedHeadersSize;
408 if (CborReadBstr(&in, &encodedProtectedHeadersSize,
409 &encodedProtectedHeadersPtr) != CBOR_READ_RESULT_OK) {
410 COSE_PRINT_ERROR("Value for encodedProtectedHeaders is not a bstr\n");
411 return false;
412 }
413 std::span encodedProtectedHeaders(encodedProtectedHeadersPtr,
414 encodedProtectedHeadersSize);
415
416 size_t unprotectedHeadersSize;
417 if (CborReadMap(&in, &unprotectedHeadersSize) != CBOR_READ_RESULT_OK) {
418 COSE_PRINT_ERROR("Value for unprotectedHeaders is not a map\n");
419 return false;
420 }
421
422 /* skip past unprotected headers by reading two items per map entry */
423 for (size_t item = 0; item < 2 * unprotectedHeadersSize; item++) {
424 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
425 COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n");
426 return false;
427 }
428 }
429
430 const uint8_t* dataPtr;
431 size_t dataSize = 0;
432 if (CborReadBstr(&in, &dataSize, &dataPtr) != CBOR_READ_RESULT_OK) {
433 if (CborReadNull(&in) != CBOR_READ_RESULT_OK) {
434 COSE_PRINT_ERROR("Value for payload is not null or a bstr\n");
435 return false;
436 }
437 }
438 std::vector<uint8_t> data(dataPtr, dataPtr + dataSize);
439
440 if (data.size() > 0 && detachedContent.size() > 0) {
441 COSE_PRINT_ERROR("data and detachedContent cannot both be non-empty\n");
442 return false;
443 }
444
445 const uint8_t* coseSignatureData;
446 size_t coseSignatureSize;
447 if (CborReadBstr(&in, &coseSignatureSize, &coseSignatureData) !=
448 CBOR_READ_RESULT_OK) {
449 COSE_PRINT_ERROR("Value for signature is not a bstr\n");
450 return false;
451 }
452
453 if (coseSignatureSize != kEcdsaSignatureSize) {
454 COSE_PRINT_ERROR("COSE signature length is %zu, expected %zu\n",
455 coseSignatureSize, kEcdsaSignatureSize);
456 return false;
457 }
458
459 // The last field is the payload, independently of how it's transported (RFC
460 // 8152 section 4.4). Since our API specifies only one of |data| and
461 // |detachedContent| can be non-empty, it's simply just the non-empty one.
462 auto& signaturePayload = data.size() > 0 ? data : detachedContent;
463
464 std::vector<uint8_t> toBeSigned =
465 coseBuildToBeSigned(encodedProtectedHeaders, signaturePayload);
466 if (!checkEcDsaSignature(sha(toBeSigned), coseSignatureData,
467 publicKey.data(), publicKey.size())) {
468 COSE_PRINT_ERROR("Signature check failed\n");
469 return false;
470 }
471
472 return true;
473 }
474
475 /*
476 * Strict signature verification code
477 */
478 static const uint8_t kSignatureHeader[] = {
479 /* clang-format off */
480 0xD2, // 0xc0 = Tagged item | tag = 18 = COSE_TAG_SIGN1
481 0x84, // 0x80 = Array | len = 4
482
483 // Array item 1
484 #ifdef APPLOADER_PACKAGE_SIGN_P384
485 0x55, // 0x20 = Byte string | len = 21
486 #else
487 0x54, // 0x20 = Byte string | len = 20
488 #endif
489
490 0xA2, // 0xa0 = Map | items = 2
491
492 // Map entry 1: key, value
493 0x01, // 0x0 = unsigned int | val = 1 = COSE_LABEL_ALG
494 #ifdef APPLOADER_PACKAGE_SIGN_P384
495 0x38, // 0x2 = Negative int | additional = 24 (1 byte val)
496 0x22, // Value = 34
497 // == -1 - 34 = -35 = COSE_ALG_ECDSA_384
498 #else
499 0x26, // 0x2 = Negative int | value = 6
500 // == -1 - 6 = -7 = COSE_ALG_ECDSA_256
501 #endif
502 // Map entry 2: key, value
503 0x3A, // 0x3 = Negative int | additional = 26 = next 4 bytes
504 0x00, // 0x00010000 = 65536
505 0x01, // -1 - 65536 = -65535 = COSE_LABEL_TRUSTY
506 0x00,
507 0x00,
508
509 0x82, // 0x80 = Array | len = 2
510 0x69, // 0x30 = Text string | len = 9
511 0x54, // T
512 0x72, // r
513 0x75, // u
514 0x73, // s
515 0x74, // t
516 0x79, // y
517 0x41, // A
518 0x70, // p
519 0x70, // p
520 // Version
521 0x01, // 0x00 = Small value | value = 1 = APPLOADER_SIGNATURE_FORMAT_VERSION_CURRENT
522
523 // Array Item 2
524 0xA1, // 0xa = Map | items = 1
525 0x04, // 0x0 = unsigned int | value = 4 = COSE_LABEL_KID
526 0x41, // 0x4 = byte string | len = 1
527 /* Next octet is the key Id */
528
529 /* clang-format on */
530 };
531
532 static const uint8_t kSignatureHeaderPart2[] = {
533 /* clang-format off */
534 0xF6, // 0x7 = simple value | value = 22 = null
535 0x58, // 0x2 = bytes string | additional = 24 = next 1 byte
536 #ifdef APPLOADER_PACKAGE_SIGN_P384
537 0x60 // length = 96
538 #else
539 0x40 // length = 64
540 #endif
541 /* clang-format on */
542 };
543
544 static const uint8_t kSignature1Header[] = {
545 /* clang-format off */
546 0x84, // 0x8 = array | length = 4
547
548 // Array item 1
549 0x6A, // 0x6 = text string | length = 10
550 0x53, // S
551 0x69, // i
552 0x67, // g
553 0x6E, // n
554 0x61, // a
555 0x74, // t
556 0x75, // u
557 0x72, // r
558 0x65, // e
559 0x31, // 1
560
561 // Array item 2
562 #ifdef APPLOADER_PACKAGE_SIGN_P384
563 0x55, // 0x20 = Byte string | len = 21
564 #else
565 0x54, // 0x20 = Byte string | len = 20
566 #endif
567 0xA2, // 0xa0 = Map | items = 2
568
569 // Map entry 1: key, value
570 0x01, // 0x0 = unsigned int | val = 1 = COSE_LABEL_ALG
571 #ifdef APPLOADER_PACKAGE_SIGN_P384
572 0x38, // 0x2 = Negative int | additional = 24 (1 byte val)
573 0x22, // Value = 34
574 // == -1 - 34 = -35 = COSE_ALG_ECDSA_384
575 #else
576 0x26, // 0x2 = Negative int | value = 6
577 // == -1 - 6 = -7 = COSE_ALG_ECDSA_256
578 #endif
579 // Map entry 2: key, value
580 0x3A, // 0x3 = Negative int | additional = 26 = next 4 bytes
581 0x00, // 0x00010000 = 65536
582 0x01, // -1 - 65536 = -65535 = COSE_LABEL_TRUSTY
583 0x00,
584 0x00,
585
586 0x82, // 0x8 = Array | len = 2
587 0x69, // 0x30 = Text string | len = 9
588 0x54, // T
589 0x72, // r
590 0x75, // u
591 0x73, // s
592 0x74, // t
593 0x79, // y
594 0x41, // A
595 0x70, // p
596 0x70, // p
597 // Version
598 0x01, // 0x00 = Small value | value = 1
599 // = APPLOADER_SIGNATURE_FORMAT_VERSION_CURRENT
600
601 // Array item 3
602 0x40, // 0x4 = byte string | len = 0
603
604 /* clang-format on */
605 };
606
607 /*
608 * Fixed offset constants
609 */
610 constexpr size_t kSignatureKeyIdOffset = sizeof(kSignatureHeader);
611 constexpr size_t kSignatureHeaderPart2Offset = kSignatureKeyIdOffset + 1;
612 constexpr size_t kSignatureOffset =
613 kSignatureHeaderPart2Offset + sizeof(kSignatureHeaderPart2);
614 constexpr size_t kPayloadOffset = kSignatureOffset + kEcdsaSignatureSize;
615
strictCheckEcDsaSignature(const uint8_t * packageStart,size_t packageSize,GetKeyFn keyFn,const uint8_t ** outPackageStart,size_t * outPackageSize)616 bool strictCheckEcDsaSignature(const uint8_t* packageStart,
617 size_t packageSize,
618 GetKeyFn keyFn,
619 const uint8_t** outPackageStart,
620 size_t* outPackageSize) {
621 if (packageSize < kPayloadOffset) {
622 COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not large enough\n");
623 return false;
624 }
625
626 if (CRYPTO_memcmp(packageStart, kSignatureHeader,
627 sizeof(kSignatureHeader))) {
628 COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n");
629 return false;
630 }
631
632 uint8_t kid = packageStart[kSignatureKeyIdOffset];
633 auto [publicKey, publicKeySize] = keyFn(kid);
634 if (!publicKey) {
635 COSE_PRINT_ERROR("Failed to retrieve public key\n");
636 return false;
637 }
638
639 if (CRYPTO_memcmp(packageStart + kSignatureHeaderPart2Offset,
640 kSignatureHeaderPart2, sizeof(kSignatureHeaderPart2))) {
641 COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n");
642 return false;
643 }
644
645 // The Signature1 structure encodes the payload as a bstr wrapping the
646 // actual contents (even if they already are CBOR), so we need to manually
647 // prepend a CBOR bstr header to the payload
648 constexpr size_t kMaxPayloadSizeHeaderSize = 9;
649 size_t payloadSize = packageSize - kPayloadOffset;
650 size_t payloadSizeHeaderSize = cbor::encodedSizeOf(payloadSize);
651 assert(payloadSizeHeaderSize <= kMaxPayloadSizeHeaderSize);
652
653 uint8_t payloadSizeHeader[kMaxPayloadSizeHeaderSize];
654
655 cbor::encodeBstrHeader(payloadSize, kMaxPayloadSizeHeaderSize,
656 payloadSizeHeader);
657
658 SHADigest digest = sha({{kSignature1Header, sizeof(kSignature1Header)},
659 {payloadSizeHeader, payloadSizeHeaderSize},
660 {packageStart + kPayloadOffset, payloadSize}});
661
662 if (!checkEcDsaSignature(digest, packageStart + kSignatureOffset,
663 publicKey.get(), publicKeySize)) {
664 COSE_PRINT_ERROR("Signature check failed\n");
665 return false;
666 }
667
668 if (outPackageStart != nullptr) {
669 *outPackageStart = packageStart + kPayloadOffset;
670 }
671 if (outPackageSize != nullptr) {
672 *outPackageSize = payloadSize;
673 }
674 return true;
675 }
676
coseBuildGcmAad(const std::string_view context,const std::span<const uint8_t> encodedProtectedHeaders,const std::span<const uint8_t> externalAad)677 static std::tuple<std::unique_ptr<uint8_t[]>, size_t> coseBuildGcmAad(
678 const std::string_view context,
679 const std::span<const uint8_t> encodedProtectedHeaders,
680 const std::span<const uint8_t> externalAad) {
681 cbor::ArrayCborEncoder enc;
682 enc.encodeArray([&](auto& enc) {
683 enc.encodeTstr(context);
684 enc.encodeBstr(encodedProtectedHeaders);
685 enc.encodeBstr(externalAad);
686 });
687
688 return {enc.intoVec().arr(), enc.size()};
689 }
690
encryptAesGcm(const std::vector<uint8_t> & key,const std::vector<uint8_t> & nonce,const CoseByteView & data,std::span<const uint8_t> additionalAuthenticatedData)691 static std::optional<std::vector<uint8_t>> encryptAesGcm(
692 const std::vector<uint8_t>& key,
693 const std::vector<uint8_t>& nonce,
694 const CoseByteView& data,
695 std::span<const uint8_t> additionalAuthenticatedData) {
696 if (key.size() != kAesGcmKeySize) {
697 COSE_PRINT_ERROR("key is not kAesGcmKeySize (%zu) bytes, got %zu\n",
698 kAesGcmKeySize, key.size());
699 return {};
700 }
701 if (nonce.size() != kAesGcmIvSize) {
702 COSE_PRINT_ERROR("nonce is not kAesGcmIvSize bytes, got %zu\n",
703 nonce.size());
704 return {};
705 }
706
707 // The result is the ciphertext followed by the tag (kAesGcmTagSize bytes).
708 std::vector<uint8_t> encryptedData;
709 encryptedData.resize(data.size() + kAesGcmTagSize);
710 unsigned char* ciphertext = (unsigned char*)encryptedData.data();
711 unsigned char* tag = ciphertext + data.size();
712
713 auto ctx = EVP_CIPHER_CTX_Ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
714 if (ctx.get() == nullptr) {
715 COSE_PRINT_ERROR("EVP_CIPHER_CTX_new: failed, error 0x%lx\n",
716 static_cast<unsigned long>(ERR_get_error()));
717 return {};
718 }
719
720 if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_trusty_gcm(), NULL, NULL, NULL) !=
721 1) {
722 COSE_PRINT_ERROR("EVP_EncryptInit_ex: failed, error 0x%lx\n",
723 static_cast<unsigned long>(ERR_get_error()));
724 return {};
725 }
726
727 if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, kAesGcmIvSize,
728 NULL) != 1) {
729 COSE_PRINT_ERROR(
730 "EVP_CIPHER_CTX_ctrl: failed setting nonce length, "
731 "error 0x%lx\n",
732 static_cast<unsigned long>(ERR_get_error()));
733 return {};
734 }
735
736 if (EVP_EncryptInit_ex(ctx.get(), NULL, NULL, key.data(), nonce.data()) !=
737 1) {
738 COSE_PRINT_ERROR("EVP_EncryptInit_ex: failed, error 0x%lx\n",
739 static_cast<unsigned long>(ERR_get_error()));
740 return {};
741 }
742
743 int numWritten;
744 if (additionalAuthenticatedData.size() > 0) {
745 if (EVP_EncryptUpdate(ctx.get(), NULL, &numWritten,
746 additionalAuthenticatedData.data(),
747 additionalAuthenticatedData.size()) != 1) {
748 fprintf(stderr,
749 "EVP_EncryptUpdate: failed for "
750 "additionalAuthenticatedData, error 0x%lx\n",
751 static_cast<unsigned long>(ERR_get_error()));
752 return {};
753 }
754 /*
755 * std::span::size() should return an size_type==size_t
756 * value but older versions of libcxx return an index_type
757 * which is an alias of ptrdiff_t (a signed type).
758 * We cast the size explicitly to a size_t to cover both cases.
759 */
760 if (static_cast<size_t>(numWritten) !=
761 static_cast<size_t>(additionalAuthenticatedData.size())) {
762 fprintf(stderr,
763 "EVP_EncryptUpdate: Unexpected outl=%d (expected %zu) "
764 "for additionalAuthenticatedData\n",
765 numWritten, additionalAuthenticatedData.size());
766 return {};
767 }
768 }
769
770 if (data.size() > 0) {
771 if (EVP_EncryptUpdate(ctx.get(), ciphertext, &numWritten, data.data(),
772 data.size()) != 1) {
773 COSE_PRINT_ERROR("EVP_EncryptUpdate: failed, error 0x%lx\n",
774 static_cast<unsigned long>(ERR_get_error()));
775 return {};
776 }
777 if (static_cast<size_t>(numWritten) !=
778 static_cast<size_t>(data.size())) {
779 fprintf(stderr,
780 "EVP_EncryptUpdate: Unexpected outl=%d (expected %zu)\n",
781 numWritten, data.size());
782 ;
783 return {};
784 }
785 }
786
787 if (EVP_EncryptFinal_ex(ctx.get(), ciphertext + numWritten, &numWritten) !=
788 1) {
789 COSE_PRINT_ERROR("EVP_EncryptFinal_ex: failed, error 0x%lx\n",
790 static_cast<unsigned long>(ERR_get_error()));
791 return {};
792 }
793 if (numWritten != 0) {
794 COSE_PRINT_ERROR("EVP_EncryptFinal_ex: Unexpected non-zero outl=%d\n",
795 numWritten);
796 return {};
797 }
798
799 if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kAesGcmTagSize,
800 tag) != 1) {
801 COSE_PRINT_ERROR(
802 "EVP_CIPHER_CTX_ctrl: failed getting tag, "
803 "error 0x%lx\n",
804 static_cast<unsigned long>(ERR_get_error()));
805 return {};
806 }
807
808 return encryptedData;
809 }
810
coseEncryptAesGcm(const std::string_view context,const std::vector<uint8_t> & key,const CoseByteView & data,const std::vector<uint8_t> & externalAad,const std::vector<uint8_t> & encodedProtectedHeaders,const CoseByteView & unprotectedHeaders,std::optional<std::vector<uint8_t>> recipients)811 static std::optional<std::vector<uint8_t>> coseEncryptAesGcm(
812 const std::string_view context,
813 const std::vector<uint8_t>& key,
814 const CoseByteView& data,
815 const std::vector<uint8_t>& externalAad,
816 const std::vector<uint8_t>& encodedProtectedHeaders,
817 const CoseByteView& unprotectedHeaders,
818 std::optional<std::vector<uint8_t>> recipients) {
819 std::optional<std::vector<uint8_t>> iv = getRandom(kAesGcmIvSize);
820 if (!iv) {
821 COSE_PRINT_ERROR("Error generating encryption IV\n");
822 return {};
823 }
824
825 cbor::VectorCborEncoder ivEnc;
826 ivEnc.encodeMap([&](auto& enc) {
827 enc.encodeKeyValue(COSE_LABEL_IV,
828 [&](auto& enc) { enc.encodeBstr(iv.value()); });
829 });
830
831 auto finalUnprotectedHeaders =
832 cbor::mergeMaps(unprotectedHeaders, ivEnc.view());
833 if (!finalUnprotectedHeaders) {
834 COSE_PRINT_ERROR("Error updating unprotected headers with IV\n");
835 return {};
836 }
837
838 std::span encodedProtectedHeadersView(encodedProtectedHeaders.data(),
839 encodedProtectedHeaders.size());
840 std::span externalAadView = externalAad;
841 auto [gcmAad, gcmAadSize] = coseBuildGcmAad(
842 context, encodedProtectedHeadersView, externalAadView);
843 std::span gcmAadView(gcmAad.get(), gcmAadSize);
844
845 std::optional<std::vector<uint8_t>> ciphertext =
846 encryptAesGcm(key, iv.value(), data, gcmAadView);
847 if (!ciphertext) {
848 COSE_PRINT_ERROR("Error encrypting data\n");
849 return {};
850 }
851
852 cbor::VectorCborEncoder enc;
853 enc.encodeArray([&](auto& enc) {
854 enc.encodeBstr(encodedProtectedHeaders);
855 enc.copyBytes(finalUnprotectedHeaders.value());
856 enc.encodeBstr(ciphertext.value());
857 if (recipients) {
858 enc.copyBytes(recipients.value());
859 }
860 });
861
862 return enc.intoVec();
863 }
864
coseEncryptAesGcmKeyWrap(const std::vector<uint8_t> & key,uint8_t keyId,const CoseByteView & data,const std::vector<uint8_t> & externalAad,const std::vector<uint8_t> & encodedProtectedHeaders,const CoseByteView & unprotectedHeaders,bool tagged)865 std::optional<std::vector<uint8_t>> coseEncryptAesGcmKeyWrap(
866 const std::vector<uint8_t>& key,
867 uint8_t keyId,
868 const CoseByteView& data,
869 const std::vector<uint8_t>& externalAad,
870 const std::vector<uint8_t>& encodedProtectedHeaders,
871 const CoseByteView& unprotectedHeaders,
872 bool tagged) {
873 /* Generate and encrypt the CEK */
874 std::optional<std::vector<uint8_t>> contentEncryptionKey =
875 getRandom(kAesGcmKeySize);
876 if (!contentEncryptionKey) {
877 COSE_PRINT_ERROR("Error generating encryption key\n");
878 return {};
879 }
880
881 cbor::VectorCborEncoder coseKeyEnc;
882 coseKeyEnc.encodeMap([&](auto& enc) {
883 enc.encodeKeyValue(COSE_LABEL_KEY_KTY, COSE_KEY_TYPE_SYMMETRIC);
884 enc.encodeKeyValue(COSE_LABEL_KEY_ALG, COSE_VAL_CIPHER_ALG);
885 enc.encodeKeyValue(COSE_LABEL_KEY_SYMMETRIC_KEY, [&](auto& enc) {
886 enc.encodeBstr(contentEncryptionKey.value());
887 });
888 });
889 CoseByteView coseKeyByteView = coseKeyEnc.view();
890
891 cbor::VectorCborEncoder keyUnprotectedHeadersEnc;
892 keyUnprotectedHeadersEnc.encodeMap([&](auto& enc) {
893 enc.encodeKeyValue(COSE_LABEL_KID, [&](auto& enc) {
894 enc.encodeBstr(std::span<const uint8_t>(&keyId, 1));
895 });
896 });
897 auto keyUnprotectedHeaders = keyUnprotectedHeadersEnc.view();
898
899 cbor::VectorCborEncoder encodedProtectedHeadersForEncKey;
900 encodedProtectedHeadersForEncKey.encodeMap([&](auto& enc) {
901 enc.encodeKeyValue(COSE_LABEL_ALG, COSE_VAL_CIPHER_ALG);
902 });
903
904 auto encContentEncryptionKey =
905 coseEncryptAesGcm(COSE_CONTEXT_ENC_RECIPIENT, key, coseKeyByteView,
906 {}, encodedProtectedHeadersForEncKey.intoVec(),
907 keyUnprotectedHeaders, {});
908 if (!encContentEncryptionKey.has_value()) {
909 COSE_PRINT_ERROR("Error wrapping encryption key\n");
910 return {};
911 }
912
913 cbor::VectorCborEncoder recipientsEnc;
914 recipientsEnc.encodeArray(
915 [&](auto& enc) { enc.copyBytes(encContentEncryptionKey.value()); });
916 auto recipients = recipientsEnc.intoVec();
917
918 auto coseEncrypt = coseEncryptAesGcm(
919 COSE_CONTEXT_ENCRYPT, std::move(contentEncryptionKey.value()), data,
920 externalAad, encodedProtectedHeaders, unprotectedHeaders,
921 std::move(recipients));
922 if (!coseEncrypt.has_value()) {
923 COSE_PRINT_ERROR("Error encrypting application package\n");
924 return {};
925 }
926
927 if (tagged) {
928 cbor::VectorCborEncoder enc;
929 enc.encodeTag(COSE_TAG_ENCRYPT,
930 [&](auto& enc) { enc.copyBytes(coseEncrypt.value()); });
931 return enc.intoVec();
932 } else {
933 return coseEncrypt;
934 }
935 }
936
decryptAesGcmInPlace(std::span<const uint8_t> key,std::span<const uint8_t> nonce,uint8_t * encryptedData,size_t encryptedDataSize,std::span<const uint8_t> additionalAuthenticatedData,size_t * outPlaintextSize)937 static bool decryptAesGcmInPlace(
938 std::span<const uint8_t> key,
939 std::span<const uint8_t> nonce,
940 uint8_t* encryptedData,
941 size_t encryptedDataSize,
942 std::span<const uint8_t> additionalAuthenticatedData,
943 size_t* outPlaintextSize) {
944 assert(outPlaintextSize != nullptr);
945
946 int ciphertextSize = int(encryptedDataSize) - kAesGcmTagSize;
947 if (ciphertextSize < 0) {
948 COSE_PRINT_ERROR("encryptedData too small\n");
949 return false;
950 }
951 if (key.size() != kAesGcmKeySize) {
952 COSE_PRINT_ERROR("key is not kAesGcmKeySize (%zu) bytes, got %zu\n",
953 kAesGcmKeySize, key.size());
954 return {};
955 }
956 if (nonce.size() != kAesGcmIvSize) {
957 COSE_PRINT_ERROR("nonce is not kAesGcmIvSize bytes, got %zu\n",
958 nonce.size());
959 return false;
960 }
961 unsigned char* ciphertext = encryptedData;
962 unsigned char* tag = ciphertext + ciphertextSize;
963
964 /*
965 * Decrypt the data in place. OpenSSL and BoringSSL support this as long as
966 * the plaintext buffer completely overlaps the ciphertext.
967 */
968 unsigned char* plaintext = encryptedData;
969
970 auto ctx = EVP_CIPHER_CTX_Ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
971 if (ctx.get() == nullptr) {
972 COSE_PRINT_ERROR("EVP_CIPHER_CTX_new: failed, error 0x%lx\n",
973 static_cast<unsigned long>(ERR_get_error()));
974 return false;
975 }
976
977 if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_trusty_gcm(), NULL, NULL, NULL) !=
978 1) {
979 COSE_PRINT_ERROR("EVP_DecryptInit_ex: failed, error 0x%lx\n",
980 static_cast<unsigned long>(ERR_get_error()));
981 return false;
982 }
983
984 if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, kAesGcmIvSize,
985 NULL) != 1) {
986 COSE_PRINT_ERROR(
987 "EVP_CIPHER_CTX_ctrl: failed setting nonce length, "
988 "error 0x%lx\n",
989 static_cast<unsigned long>(ERR_get_error()));
990 return false;
991 }
992
993 if (EVP_DecryptInit_ex(ctx.get(), NULL, NULL, key.data(), nonce.data()) !=
994 1) {
995 COSE_PRINT_ERROR("EVP_DecryptInit_ex: failed, error 0x%lx\n",
996 static_cast<unsigned long>(ERR_get_error()));
997 return false;
998 }
999
1000 int numWritten;
1001 if (additionalAuthenticatedData.size() > 0) {
1002 if (EVP_DecryptUpdate(ctx.get(), NULL, &numWritten,
1003 additionalAuthenticatedData.data(),
1004 additionalAuthenticatedData.size()) != 1) {
1005 COSE_PRINT_ERROR(
1006 "EVP_DecryptUpdate: failed for "
1007 "additionalAuthenticatedData, error 0x%lx\n",
1008 static_cast<unsigned long>(ERR_get_error()));
1009 return false;
1010 }
1011 if (static_cast<size_t>(numWritten) !=
1012 static_cast<size_t>(additionalAuthenticatedData.size())) {
1013 COSE_PRINT_ERROR(
1014 "EVP_DecryptUpdate: Unexpected outl=%d "
1015 "(expected %zd) for additionalAuthenticatedData\n",
1016 numWritten, additionalAuthenticatedData.size());
1017 return false;
1018 }
1019 }
1020
1021 if (EVP_DecryptUpdate(ctx.get(), plaintext, &numWritten, ciphertext,
1022 ciphertextSize) != 1) {
1023 COSE_PRINT_ERROR("EVP_DecryptUpdate: failed, error 0x%lx\n",
1024 static_cast<unsigned long>(ERR_get_error()));
1025 return false;
1026 }
1027 if (numWritten != ciphertextSize) {
1028 COSE_PRINT_ERROR(
1029 "EVP_DecryptUpdate: Unexpected outl=%d "
1030 "(expected %d)\n",
1031 numWritten, ciphertextSize);
1032 return false;
1033 }
1034
1035 if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kAesGcmTagSize,
1036 tag)) {
1037 COSE_PRINT_ERROR(
1038 "EVP_CIPHER_CTX_ctrl: failed setting expected tag, "
1039 "error 0x%lx\n",
1040 static_cast<unsigned long>(ERR_get_error()));
1041 return false;
1042 }
1043
1044 int ret =
1045 EVP_DecryptFinal_ex(ctx.get(), plaintext + numWritten, &numWritten);
1046 if (ret != 1) {
1047 COSE_PRINT_ERROR("EVP_DecryptFinal_ex: failed, error 0x%lx\n",
1048 static_cast<unsigned long>(ERR_get_error()));
1049 return false;
1050 }
1051 if (numWritten != 0) {
1052 COSE_PRINT_ERROR("EVP_DecryptFinal_ex: Unexpected non-zero outl=%d\n",
1053 numWritten);
1054 return false;
1055 }
1056
1057 *outPlaintextSize = ciphertextSize;
1058 return true;
1059 }
1060
coseDecryptAesGcmInPlace(const std::string_view context,const CoseByteView & item,const std::span<const uint8_t> key,const std::vector<uint8_t> & externalAad,const uint8_t ** outPlaintextStart,size_t * outPlaintextSize,DecryptFn keyDecryptFn)1061 static bool coseDecryptAesGcmInPlace(const std::string_view context,
1062 const CoseByteView& item,
1063 const std::span<const uint8_t> key,
1064 const std::vector<uint8_t>& externalAad,
1065 const uint8_t** outPlaintextStart,
1066 size_t* outPlaintextSize,
1067 DecryptFn keyDecryptFn) {
1068 assert(outPlaintextStart != nullptr);
1069 assert(outPlaintextSize != nullptr);
1070
1071 struct CborIn in;
1072 CborInInit(item.data(), item.size(), &in);
1073
1074 size_t num_elements;
1075 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
1076 COSE_PRINT_ERROR("Encrypted data is not a CBOR array\n");
1077 return false;
1078 }
1079
1080 if (num_elements < 3 || num_elements > 4) {
1081 COSE_PRINT_ERROR("Invalid COSE encryption array size, got %zu\n",
1082 num_elements);
1083 return false;
1084 }
1085
1086 const uint8_t* enc_protected_headers_data;
1087 size_t enc_protected_headers_size;
1088 if (CborReadBstr(&in, &enc_protected_headers_size,
1089 &enc_protected_headers_data) != CBOR_READ_RESULT_OK) {
1090 COSE_PRINT_ERROR(
1091 "Failed to retrieve protected headers "
1092 "from COSE encryption structure\n");
1093 return false;
1094 }
1095
1096 struct CborIn protHdrIn;
1097 CborInInit(enc_protected_headers_data, enc_protected_headers_size,
1098 &protHdrIn);
1099
1100 size_t numPairs;
1101 if (CborReadMap(&protHdrIn, &numPairs) != CBOR_READ_RESULT_OK) {
1102 COSE_PRINT_ERROR("Invalid protected headers CBOR type\n");
1103 return false;
1104 }
1105
1106 int64_t label;
1107 std::optional<uint64_t> alg;
1108 for (size_t i = 0; i < numPairs; i++) {
1109 // Read key
1110 if (CborReadInt(&protHdrIn, &label) != CBOR_READ_RESULT_OK) {
1111 COSE_PRINT_ERROR(
1112 "Failed to read protected headers "
1113 "in COSE encryption structure\n");
1114 return false;
1115 }
1116
1117 // Read value
1118 if (label == COSE_LABEL_ALG) {
1119 uint64_t algVal;
1120 if (CborReadUint(&protHdrIn, &algVal) != CBOR_READ_RESULT_OK) {
1121 COSE_PRINT_ERROR(
1122 "Wrong CBOR type for alg value in unprotected headers\n");
1123 return false;
1124 }
1125
1126 if (algVal != COSE_VAL_CIPHER_ALG) {
1127 COSE_PRINT_ERROR("Invalid COSE algorithm, got %" PRId64 "\n",
1128 algVal);
1129 return false;
1130 }
1131
1132 alg = algVal;
1133 } else if (CborReadSkip(&protHdrIn) != CBOR_READ_RESULT_OK) {
1134 COSE_PRINT_ERROR(
1135 "Failed to read protected headers "
1136 "in COSE encryption structure\n");
1137 return false;
1138 }
1139 }
1140
1141 if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
1142 COSE_PRINT_ERROR(
1143 "Failed to retrieve unprotected headers "
1144 "from COSE encryption structure\n");
1145 return false;
1146 }
1147
1148 const uint8_t* ivData = nullptr;
1149 size_t ivSize;
1150 for (size_t i = 0; i < numPairs; i++) {
1151 // Read key
1152 if (CborReadInt(&in, &label) != CBOR_READ_RESULT_OK) {
1153 COSE_PRINT_ERROR(
1154 "Failed to read unprotected headers "
1155 "in COSE encryption structure\n");
1156 return false;
1157 }
1158
1159 // Read value
1160 if (label == COSE_LABEL_IV) {
1161 if (CborReadBstr(&in, &ivSize, &ivData) != CBOR_READ_RESULT_OK) {
1162 COSE_PRINT_ERROR(
1163 "Wrong CBOR type for IV value in unprotected headers\n");
1164 return false;
1165 }
1166 } else if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
1167 COSE_PRINT_ERROR(
1168 "Failed to read unprotected headers "
1169 "in COSE encryption structure\n");
1170 return false;
1171 }
1172 }
1173
1174 if (ivData == nullptr) {
1175 COSE_PRINT_ERROR("Missing IV field in COSE encryption structure\n");
1176 return false;
1177 }
1178
1179 const uint8_t* ciphertextData;
1180 size_t ciphertextSize;
1181 if (CborReadBstr(&in, &ciphertextSize, &ciphertextData) !=
1182 CBOR_READ_RESULT_OK) {
1183 COSE_PRINT_ERROR(
1184 "Failed to retrieve ciphertext "
1185 "from COSE encryption structure\n");
1186 return false;
1187 }
1188
1189 std::span externalAadView = externalAad;
1190 std::span encodedProtectedHeaders(enc_protected_headers_data,
1191 enc_protected_headers_size);
1192 auto [gcmAad, gcmAadSize] =
1193 coseBuildGcmAad(context, encodedProtectedHeaders, externalAadView);
1194
1195 std::span gcmAadView(gcmAad.get(), gcmAadSize);
1196 std::span ivView(ivData, ivSize);
1197 if (!keyDecryptFn(key, ivView, const_cast<uint8_t*>(ciphertextData),
1198 ciphertextSize, gcmAadView, outPlaintextSize)) {
1199 return false;
1200 }
1201
1202 *outPlaintextStart = ciphertextData;
1203
1204 return true;
1205 }
1206
coseDecryptAesGcmKeyWrapInPlace(const CoseByteView & cose_encrypt,GetKeyFn keyFn,const std::vector<uint8_t> & externalAad,bool checkTag,const uint8_t ** outPackageStart,size_t * outPackageSize,DecryptFn keyDecryptFn)1207 bool coseDecryptAesGcmKeyWrapInPlace(const CoseByteView& cose_encrypt,
1208 GetKeyFn keyFn,
1209 const std::vector<uint8_t>& externalAad,
1210 bool checkTag,
1211 const uint8_t** outPackageStart,
1212 size_t* outPackageSize,
1213 DecryptFn keyDecryptFn) {
1214 assert(outPackageStart != nullptr);
1215 assert(outPackageSize != nullptr);
1216
1217 if (!keyDecryptFn) {
1218 keyDecryptFn = &decryptAesGcmInPlace;
1219 }
1220
1221 struct CborIn in;
1222 CborInInit(cose_encrypt.data(), cose_encrypt.size(), &in);
1223
1224 uint64_t tag;
1225 if (CborReadTag(&in, &tag) == CBOR_READ_RESULT_OK) {
1226 if (checkTag && tag != COSE_TAG_ENCRYPT) {
1227 TLOGE("Invalid COSE_Encrypt semantic tag: %" PRIu64 "\n", tag);
1228 return false;
1229 }
1230 } else if (checkTag) {
1231 TLOGE("Expected COSE_Encrypt semantic tag\n");
1232 return false;
1233 }
1234
1235 size_t num_elements;
1236 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
1237 COSE_PRINT_ERROR("Encrypted data is not a CBOR array\n");
1238 return false;
1239 }
1240
1241 if (num_elements != kCoseEncryptArrayElements) {
1242 COSE_PRINT_ERROR("Invalid COSE_Encrypt array size, got %zu\n",
1243 num_elements);
1244 return false;
1245 }
1246
1247 // Skip past the first three array elemements
1248 while (num_elements-- > 1) {
1249 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
1250 COSE_PRINT_ERROR(
1251 "Failed to retrieve recipients "
1252 "from COSE_Encrypt structure\n");
1253 return false;
1254 }
1255 }
1256
1257 // Read recipients array
1258 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
1259 COSE_PRINT_ERROR(
1260 "Failed to retrieve recipients "
1261 "from COSE_Encrypt structure\n");
1262 return false;
1263 }
1264
1265 if (num_elements != 1) {
1266 COSE_PRINT_ERROR("Invalid recipients array size, got %zu\n",
1267 num_elements);
1268 return false;
1269 }
1270
1271 const size_t recipientOffset = CborInOffset(&in);
1272 // Read singleton recipient
1273 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
1274 COSE_PRINT_ERROR("COSE_Recipient is not a CBOR array\n");
1275 return false;
1276 }
1277
1278 if (num_elements != 3) {
1279 COSE_PRINT_ERROR(
1280 "Invalid COSE_Recipient structure array size, "
1281 "got %zu\n",
1282 num_elements);
1283 return false;
1284 }
1285
1286 // Skip to unprotected headers array element
1287 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
1288 COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n");
1289 return false;
1290 }
1291
1292 size_t numPairs;
1293 if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
1294 COSE_PRINT_ERROR(
1295 "Failed to retrieve unprotected headers "
1296 "from COSE_Recipient structure\n");
1297 return false;
1298 }
1299
1300 uint64_t label;
1301 const uint8_t* keyIdBytes = nullptr;
1302 size_t keyIdSize;
1303 for (size_t i = 0; i < numPairs; i++) {
1304 // Read key
1305 if (CborReadUint(&in, &label) != CBOR_READ_RESULT_OK) {
1306 COSE_PRINT_ERROR(
1307 "Failed to read unprotected headers "
1308 "in COSE_Recipient structure\n");
1309 return false;
1310 }
1311
1312 // Read value
1313 if (label == COSE_LABEL_KID) {
1314 if (CborReadBstr(&in, &keyIdSize, &keyIdBytes) !=
1315 CBOR_READ_RESULT_OK) {
1316 COSE_PRINT_ERROR(
1317 "Failed to extract key id from unprotected headers "
1318 "in COSE_Recipient structure\n");
1319 return false;
1320 }
1321 } else if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
1322 COSE_PRINT_ERROR(
1323 "Failed to read unprotected headers "
1324 "in COSE_Recipient structure\n");
1325 return false;
1326 }
1327 }
1328
1329 // Skip over ciphertext
1330 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
1331 COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n");
1332 return false;
1333 }
1334
1335 if (!CborInAtEnd(&in)) {
1336 COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n");
1337 return false;
1338 }
1339
1340 CoseByteView recipient(cose_encrypt.data() + recipientOffset,
1341 CborInOffset(&in) - recipientOffset);
1342
1343 if (keyIdBytes == nullptr) {
1344 COSE_PRINT_ERROR("Missing key id field in COSE_Recipient\n");
1345 return false;
1346 }
1347
1348 if (keyIdSize != 1) {
1349 COSE_PRINT_ERROR("Invalid key id field length, got %zu\n", keyIdSize);
1350 return false;
1351 }
1352
1353 auto [keyEncryptionKeyStart, keyEncryptionKeySize] = keyFn(keyIdBytes[0]);
1354 if (!keyEncryptionKeyStart) {
1355 COSE_PRINT_ERROR("Failed to retrieve decryption key\n");
1356 return false;
1357 }
1358
1359 std::span keyEncryptionKey(keyEncryptionKeyStart.get(),
1360 keyEncryptionKeySize);
1361
1362 const uint8_t* coseKeyStart;
1363 size_t coseKeySize;
1364 if (!coseDecryptAesGcmInPlace(COSE_CONTEXT_ENC_RECIPIENT, recipient,
1365 keyEncryptionKey, {}, &coseKeyStart,
1366 &coseKeySize, keyDecryptFn)) {
1367 COSE_PRINT_ERROR("Failed to decrypt COSE_Key structure\n");
1368 return false;
1369 }
1370
1371 CborInInit(coseKeyStart, coseKeySize, &in);
1372 if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
1373 COSE_PRINT_ERROR("COSE_Key structure is not a map\n");
1374 return false;
1375 }
1376
1377 int64_t keyLabel;
1378 int64_t value;
1379 bool ktyValidated = false;
1380 bool algValidated = false;
1381 const uint8_t* contentEncryptionKeyStart = nullptr;
1382 size_t contentEncryptionKeySize = 0;
1383 for (size_t i = 0; i < numPairs; i++) {
1384 if (CborReadInt(&in, &keyLabel) != CBOR_READ_RESULT_OK) {
1385 COSE_PRINT_ERROR("Failed to parse key in COSE_Key structure\n");
1386 return false;
1387 }
1388
1389 switch (keyLabel) {
1390 case COSE_LABEL_KEY_KTY:
1391 if (CborReadInt(&in, &value) != CBOR_READ_RESULT_OK) {
1392 COSE_PRINT_ERROR("Wrong CBOR type for kty field of COSE_Key\n");
1393 return false;
1394 }
1395 if (value != COSE_KEY_TYPE_SYMMETRIC) {
1396 COSE_PRINT_ERROR("Invalid COSE_Key key type: %" PRId64 "\n",
1397 value);
1398 return false;
1399 }
1400 ktyValidated = true;
1401 break;
1402 case COSE_LABEL_KEY_ALG:
1403 if (CborReadInt(&in, &value) != CBOR_READ_RESULT_OK) {
1404 COSE_PRINT_ERROR("Wrong CBOR type for kty field of COSE_Key\n");
1405 return false;
1406 }
1407 if (value != COSE_VAL_CIPHER_ALG) {
1408 COSE_PRINT_ERROR("Invalid COSE_Key algorithm value: %" PRId64
1409 "\n",
1410 value);
1411 return false;
1412 }
1413 algValidated = true;
1414 break;
1415 case COSE_LABEL_KEY_SYMMETRIC_KEY:
1416 if (CborReadBstr(&in, &contentEncryptionKeySize,
1417 &contentEncryptionKeyStart)) {
1418 COSE_PRINT_ERROR("Wrong CBOR type for key field of COSE_Key\n");
1419 return false;
1420 }
1421 if (contentEncryptionKeySize != kAesGcmKeySize) {
1422 COSE_PRINT_ERROR(
1423 "Invalid content encryption key size, got %zu\n",
1424 contentEncryptionKeySize);
1425 return false;
1426 }
1427 break;
1428 default:
1429 COSE_PRINT_ERROR("Invalid key field in COSE_Key: %" PRId64 "\n",
1430 label);
1431 return false;
1432 break;
1433 }
1434 }
1435
1436 if (!ktyValidated) {
1437 COSE_PRINT_ERROR("Missing kty field of COSE_Key\n");
1438 return false;
1439 } else if (!algValidated) {
1440 COSE_PRINT_ERROR("Missing alg field of COSE_Key\n");
1441 return false;
1442 } else if (!contentEncryptionKeyStart) {
1443 COSE_PRINT_ERROR("Missing key field in COSE_Key\n");
1444 return false;
1445 }
1446
1447 const CoseByteView contentEncryptionKey(contentEncryptionKeyStart,
1448 contentEncryptionKeySize);
1449 if (!coseDecryptAesGcmInPlace(COSE_CONTEXT_ENCRYPT, cose_encrypt,
1450 contentEncryptionKey, externalAad,
1451 outPackageStart, outPackageSize,
1452 decryptAesGcmInPlace)) {
1453 COSE_PRINT_ERROR("Failed to decrypt payload\n");
1454 return false;
1455 }
1456
1457 return true;
1458 }
1459
coseGetCipherAlg(void)1460 const char* coseGetCipherAlg(void) {
1461 #ifdef APPLOADER_PACKAGE_CIPHER_A256
1462 return "AES-GCM with 256-bit key, 128-bit tag";
1463 #else
1464 return "AES-GCM with 128-bit key, 128-bit tag";
1465 #endif
1466 }
1467
coseGetSigningDsa(void)1468 const char* coseGetSigningDsa(void) {
1469 #ifdef APPLOADER_PACKAGE_SIGN_P384
1470 return "ECDA P-384 + SHA-384 signatures";
1471 #else
1472 return "ECDA P-256 + SHA-256 signatures";
1473 #endif
1474 }
1475