using BIGNUM_Ptr = std::unique_ptr<BIGNUM, std::function<void(BIGNUM*)>>; using EC_KEY_Ptr = std::unique_ptr<EC_KEY, std::function<void(EC_KEY*)>>; using ECDSA_SIG_Ptr =
std::unique_ptr<ECDSA_SIG, std::function<void(ECDSA_SIG*)>>; using EVP_CIPHER_CTX_Ptr =
std::unique_ptr<EVP_CIPHER_CTX, std::function<void(EVP_CIPHER_CTX*)>>;
using SHADigest = std::array<uint8_t, kEcdsaValueSize>;
static std::vector<uint8_t> coseBuildToBeSigned( const std::span<const uint8_t>& encodedProtectedHeaders, const std::vector<uint8_t>& data) {
cbor::VectorCborEncoder enc;
enc.encodeArray([&](auto& enc) {
enc.encodeTstr("Signature1");
enc.encodeBstr(encodedProtectedHeaders); // We currently don't support Externally Supplied Data (RFC 8152 // section 4.3) so external_aad is the empty bstr
enc.encodeEmptyBstr();
enc.encodeBstr(data);
});
uint64_t tag; /* COSE message tag is optional */ if (CborReadTag(&in, &tag) == CBOR_READ_RESULT_OK) { if (tag != COSE_TAG_SIGN1) {
COSE_PRINT_ERROR("Passed-in COSE_Sign1 contained invalid tag\n"); returnfalse;
}
}
size_t arraySize; if (CborReadArray(&in, &arraySize) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Value for COSE_Sign1 is not an array\n"); returnfalse;
}
if (arraySize != 4) {
COSE_PRINT_ERROR("Value for COSE_Sign1 is not an array of size 4\n"); returnfalse;
}
const uint8_t* encodedProtectedHeadersPtr;
size_t encodedProtectedHeadersSize; if (CborReadBstr(&in, &encodedProtectedHeadersSize,
&encodedProtectedHeadersPtr) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Value for encodedProtectedHeaders is not a bstr\n"); returnfalse;
}
std::span encodedProtectedHeaders(encodedProtectedHeadersPtr,
encodedProtectedHeadersSize);
size_t unprotectedHeadersSize; if (CborReadMap(&in, &unprotectedHeadersSize) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Value for unprotectedHeaders is not a map\n"); returnfalse;
}
/* skip past unprotected headers by reading two items per map entry */ for (size_t item = 0; item < 2 * unprotectedHeadersSize; item++) { if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n"); returnfalse;
}
}
const uint8_t* dataPtr;
size_t dataSize = 0; if (CborReadBstr(&in, &dataSize, &dataPtr) != CBOR_READ_RESULT_OK) { if (CborReadNull(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Value for payload is not null or a bstr\n"); returnfalse;
}
}
std::vector<uint8_t> data(dataPtr, dataPtr + dataSize);
if (data.size() > 0 && detachedContent.size() > 0) {
COSE_PRINT_ERROR("data and detachedContent cannot both be non-empty\n"); returnfalse;
}
const uint8_t* coseSignatureData;
size_t coseSignatureSize; if (CborReadBstr(&in, &coseSignatureSize, &coseSignatureData) !=
CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Value for signature is not a bstr\n"); returnfalse;
}
if (coseSignatureSize != kEcdsaSignatureSize) {
COSE_PRINT_ERROR("COSE signature length is %zu, expected %zu\n",
coseSignatureSize, kEcdsaSignatureSize); returnfalse;
}
// The last field is the payload, independently of how it's transported (RFC // 8152 section 4.4). Since our API specifies only one of |data| and // |detachedContent| can be non-empty, it's simply just the non-empty one. auto& signaturePayload = data.size() > 0 ? data : detachedContent;
0x82, // 0x80 = Array | len = 2 0x69, // 0x30 = Text string | len = 9 0x54, // T 0x72, // r 0x75, // u 0x73, // s 0x74, // t 0x79, // y 0x41, // A 0x70, // p 0x70, // p // Version 0x01, // 0x00 = Small value | value = 1 = APPLOADER_SIGNATURE_FORMAT_VERSION_CURRENT
// Array Item 2 0xA1, // 0xa = Map | items = 1 0x04, // 0x0 = unsigned int | value = 4 = COSE_LABEL_KID 0x41, // 0x4 = byte string | len = 1 /* Next octet is the key Id */
// Array item 1 0x6A, // 0x6 = text string | length = 10 0x53, // S 0x69, // i 0x67, // g 0x6E, // n 0x61, // a 0x74, // t 0x75, // u 0x72, // r 0x65, // e 0x31, // 1
0x82, // 0x8 = Array | len = 2 0x69, // 0x30 = Text string | len = 9 0x54, // T 0x72, // r 0x75, // u 0x73, // s 0x74, // t 0x79, // y 0x41, // A 0x70, // p 0x70, // p // Version 0x01, // 0x00 = Small value | value = 1 // = APPLOADER_SIGNATURE_FORMAT_VERSION_CURRENT
bool strictCheckEcDsaSignature(const uint8_t* packageStart,
size_t packageSize,
GetKeyFn keyFn, const uint8_t** outPackageStart,
size_t* outPackageSize) { if (packageSize < kPayloadOffset) {
COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not large enough\n"); returnfalse;
}
if (CRYPTO_memcmp(packageStart, kSignatureHeader, sizeof(kSignatureHeader))) {
COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n"); returnfalse;
}
uint8_t kid = packageStart[kSignatureKeyIdOffset]; auto [publicKey, publicKeySize] = keyFn(kid); if (!publicKey) {
COSE_PRINT_ERROR("Failed to retrieve public key\n"); returnfalse;
}
if (CRYPTO_memcmp(packageStart + kSignatureHeaderPart2Offset,
kSignatureHeaderPart2, sizeof(kSignatureHeaderPart2))) {
COSE_PRINT_ERROR("Passed-in COSE_Sign1 is not valid CBOR\n"); returnfalse;
}
// The Signature1 structure encodes the payload as a bstr wrapping the // actual contents (even if they already are CBOR), so we need to manually // prepend a CBOR bstr header to the payload
constexpr size_t kMaxPayloadSizeHeaderSize = 9;
size_t payloadSize = packageSize - kPayloadOffset;
size_t payloadSizeHeaderSize = cbor::encodedSizeOf(payloadSize);
assert(payloadSizeHeaderSize <= kMaxPayloadSizeHeaderSize);
static std::optional<std::vector<uint8_t>> encryptAesGcm( const std::vector<uint8_t>& key, const std::vector<uint8_t>& nonce, const CoseByteView& data,
std::span<const uint8_t> additionalAuthenticatedData) { if (key.size() != kAesGcmKeySize) {
COSE_PRINT_ERROR("key is not kAesGcmKeySize (%zu) bytes, got %zu\n",
kAesGcmKeySize, key.size()); return {};
} if (nonce.size() != kAesGcmIvSize) {
COSE_PRINT_ERROR("nonce is not kAesGcmIvSize bytes, got %zu\n",
nonce.size()); return {};
}
// The result is the ciphertext followed by the tag (kAesGcmTagSize bytes).
std::vector<uint8_t> encryptedData;
encryptedData.resize(data.size() + kAesGcmTagSize); unsignedchar* ciphertext = (unsignedchar*)encryptedData.data(); unsignedchar* tag = ciphertext + data.size();
auto ctx = EVP_CIPHER_CTX_Ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); if (ctx.get() == nullptr) {
COSE_PRINT_ERROR("EVP_CIPHER_CTX_new: failed, error 0x%lx\n", static_cast<unsignedlong>(ERR_get_error())); return {};
}
size_t num_elements; if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Encrypted data is not a CBOR array\n"); returnfalse;
}
int64_t label;
std::optional<uint64_t> alg; for (size_t i = 0; i < numPairs; i++) { // Read key if (CborReadInt(&protHdrIn, &label) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to read protected headers " "in COSE encryption structure\n"); returnfalse;
}
// Read value if (label == COSE_LABEL_ALG) {
uint64_t algVal; if (CborReadUint(&protHdrIn, &algVal) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Wrong CBOR type for alg value in unprotected headers\n"); returnfalse;
}
if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to retrieve unprotected headers " "from COSE encryption structure\n"); returnfalse;
}
const uint8_t* ivData = nullptr;
size_t ivSize; for (size_t i = 0; i < numPairs; i++) { // Read key if (CborReadInt(&in, &label) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to read unprotected headers " "in COSE encryption structure\n"); returnfalse;
}
// Read value if (label == COSE_LABEL_IV) { if (CborReadBstr(&in, &ivSize, &ivData) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Wrong CBOR type for IV value in unprotected headers\n"); returnfalse;
}
} elseif (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to read unprotected headers " "in COSE encryption structure\n"); returnfalse;
}
}
if (ivData == nullptr) {
COSE_PRINT_ERROR("Missing IV field in COSE encryption structure\n"); returnfalse;
}
size_t num_elements; if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Encrypted data is not a CBOR array\n"); returnfalse;
}
// Skip past the first three array elemements while (num_elements-- > 1) { if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to retrieve recipients " "from COSE_Encrypt structure\n"); returnfalse;
}
}
// Skip to unprotected headers array element if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n"); returnfalse;
}
size_t numPairs; if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to retrieve unprotected headers " "from COSE_Recipient structure\n"); returnfalse;
}
uint64_t label; const uint8_t* keyIdBytes = nullptr;
size_t keyIdSize; for (size_t i = 0; i < numPairs; i++) { // Read key if (CborReadUint(&in, &label) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to read unprotected headers " "in COSE_Recipient structure\n"); returnfalse;
}
// Read value if (label == COSE_LABEL_KID) { if (CborReadBstr(&in, &keyIdSize, &keyIdBytes) !=
CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to extract key id from unprotected headers " "in COSE_Recipient structure\n"); returnfalse;
}
} elseif (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR( "Failed to read unprotected headers " "in COSE_Recipient structure\n"); returnfalse;
}
}
// Skip over ciphertext if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n"); returnfalse;
}
if (!CborInAtEnd(&in)) {
COSE_PRINT_ERROR("Failed to read COSE_Recipient structure\n"); returnfalse;
}
if (keyIdBytes == nullptr) {
COSE_PRINT_ERROR("Missing key id field in COSE_Recipient\n"); returnfalse;
}
if (keyIdSize != 1) {
COSE_PRINT_ERROR("Invalid key id field length, got %zu\n", keyIdSize); returnfalse;
}
auto [keyEncryptionKeyStart, keyEncryptionKeySize] = keyFn(keyIdBytes[0]); if (!keyEncryptionKeyStart) {
COSE_PRINT_ERROR("Failed to retrieve decryption key\n"); returnfalse;
}
CborInInit(coseKeyStart, coseKeySize, &in); if (CborReadMap(&in, &numPairs) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("COSE_Key structure is not a map\n"); returnfalse;
}
int64_t keyLabel;
int64_t value; bool ktyValidated = false; bool algValidated = false; const uint8_t* contentEncryptionKeyStart = nullptr;
size_t contentEncryptionKeySize = 0; for (size_t i = 0; i < numPairs; i++) { if (CborReadInt(&in, &keyLabel) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Failed to parse key in COSE_Key structure\n"); returnfalse;
}
switch (keyLabel) { case COSE_LABEL_KEY_KTY: if (CborReadInt(&in, &value) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Wrong CBOR type for kty field of COSE_Key\n"); returnfalse;
} if (value != COSE_KEY_TYPE_SYMMETRIC) {
COSE_PRINT_ERROR("Invalid COSE_Key key type: %" PRId64 "\n",
value); returnfalse;
}
ktyValidated = true; break; case COSE_LABEL_KEY_ALG: if (CborReadInt(&in, &value) != CBOR_READ_RESULT_OK) {
COSE_PRINT_ERROR("Wrong CBOR type for kty field of COSE_Key\n"); returnfalse;
} if (value != COSE_VAL_CIPHER_ALG) {
COSE_PRINT_ERROR("Invalid COSE_Key algorithm value: %" PRId64 "\n",
value); returnfalse;
}
algValidated = true; break; case COSE_LABEL_KEY_SYMMETRIC_KEY: if (CborReadBstr(&in, &contentEncryptionKeySize,
&contentEncryptionKeyStart)) {
COSE_PRINT_ERROR("Wrong CBOR type for key field of COSE_Key\n"); returnfalse;
} if (contentEncryptionKeySize != kAesGcmKeySize) {
COSE_PRINT_ERROR( "Invalid content encryption key size, got %zu\n",
contentEncryptionKeySize); returnfalse;
} break; default:
COSE_PRINT_ERROR("Invalid key field in COSE_Key: %" PRId64 "\n",
label); returnfalse; break;
}
}
if (!ktyValidated) {
COSE_PRINT_ERROR("Missing kty field of COSE_Key\n"); returnfalse;
} elseif (!algValidated) {
COSE_PRINT_ERROR("Missing alg field of COSE_Key\n"); returnfalse;
} elseif (!contentEncryptionKeyStart) {
COSE_PRINT_ERROR("Missing key field in COSE_Key\n"); returnfalse;
}
const CoseByteView contentEncryptionKey(contentEncryptionKeyStart,
contentEncryptionKeySize); if (!coseDecryptAesGcmInPlace(COSE_CONTEXT_ENCRYPT, cose_encrypt,
contentEncryptionKey, externalAad,
outPackageStart, outPackageSize,
decryptAesGcmInPlace)) {
COSE_PRINT_ERROR("Failed to decrypt payload\n"); returnfalse;
}
returntrue;
}
constchar* coseGetCipherAlg(void) { #ifdef APPLOADER_PACKAGE_CIPHER_A256 return"AES-GCM with 256-bit key, 128-bit tag"; #else return"AES-GCM with 128-bit key, 128-bit tag"; #endif
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.