/* * * Copyright (c) 2020-2023 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * PSA Crypto API based implementation of CHIP crypto primitives * with Silicon Labs SDK modifications */ // The psa_driver_wrappers.h file that we're including here assumes that it has // access to private struct members. Define this here in order to avoid // compilation errors. #define MBEDTLS_ALLOW_PRIVATE_ACCESS #include #include // Include version header to get configuration information #include #if !defined(MBEDTLS_PSA_CRYPTO_C) #error "This implementation needs PSA Crypto" #endif #if !defined(MBEDTLS_USE_PSA_CRYPTO) #error "This implementation requires that PSA Crypto keys can be used for CSR generation" #endif #include "psa/crypto.h" // Go straight for the driver wrappers for speed on plaintext keys extern "C" { #include "psa_crypto_core.h" #include "psa_crypto_driver_wrappers.h" } // Includes needed for SPAKE2+ ECP operations #include #include // Includes needed for certificate parsing #if defined(MBEDTLS_X509_CRT_PARSE_C) #include #endif // defined(MBEDTLS_X509_CRT_PARSE_C) #include #include #include #if defined(MBEDTLS_ERROR_C) #include #endif // defined(MBEDTLS_ERROR_C) #include #include #include #include #include #include #include #include #include #include namespace chip { namespace Crypto { using chip::Platform::MemoryCalloc; using chip::Platform::MemoryFree; #define MAX_ERROR_STR_LEN 128 #define NUM_BYTES_IN_SHA256_HASH 32 // In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro. #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) #define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x) #else #define CHIP_CRYPTO_PAL_PRIVATE(x) x #endif #if (MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x03010000) #define CHIP_CRYPTO_PAL_PRIVATE_X509(x) MBEDTLS_PRIVATE(x) #else #define CHIP_CRYPTO_PAL_PRIVATE_X509(x) x #endif static void _log_mbedTLS_error(int error_code) { if (error_code != 0) { #if defined(MBEDTLS_ERROR_C) char error_str[MAX_ERROR_STR_LEN]; mbedtls_strerror(error_code, error_str, sizeof(error_str)); ChipLogError(Crypto, "mbedTLS error: %s", error_str); #else // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise ChipLogError(Crypto, "mbedTLS error: -0x%04X", -static_cast(error_code)); #endif } } static void _log_PSA_error(psa_status_t status) { if (status != 0) { // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise ChipLogError(Crypto, "PSA error: %ld", status); } } static bool _isValidTagLength(size_t tag_length) { if (tag_length == 8 || tag_length == 12 || tag_length == 16) { return true; } return false; } /** * @brief Compare two times * * @param t1 First time to compare * @param t2 Second time to compare * @return int 0 If both times are idential to the second, -1 if t1 < t2, 1 if t1 > t2. */ static int timeCompare(mbedtls_x509_time * t1, mbedtls_x509_time * t2) { VerifyOrReturnValue(t1->year >= t2->year, -1); VerifyOrReturnValue(t1->year <= t2->year, 1); // Same year VerifyOrReturnValue(t1->mon >= t2->mon, -1); VerifyOrReturnValue(t1->mon <= t2->mon, 1); // Same month VerifyOrReturnValue(t1->day >= t2->day, -1); VerifyOrReturnValue(t1->day <= t2->day, 1); // Same day VerifyOrReturnValue(t1->hour >= t2->hour, -1); VerifyOrReturnValue(t1->hour <= t2->hour, 1); // Same hour VerifyOrReturnValue(t1->min >= t2->min, -1); VerifyOrReturnValue(t1->min <= t2->min, 1); // Same minute VerifyOrReturnValue(t1->sec >= t2->sec, -1); VerifyOrReturnValue(t1->sec <= t2->sec, 1); // Same second return 0; } CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; size_t output_length = 0; uint8_t * buffer = nullptr; bool allocated_buffer = false; VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(nonce_length), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); // If the ciphertext and tag outputs aren't a contiguous buffer, the PSA API requires buffer copying if (Uint8::to_uchar(ciphertext) + plaintext_length != Uint8::to_uchar(tag)) { buffer = (uint8_t *) MemoryCalloc(1, plaintext_length + tag_length); allocated_buffer = true; VerifyOrExit(buffer != nullptr, error = CHIP_ERROR_NO_MEMORY); } psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_AES); psa_set_key_bits(&attr, sizeof(Symmetric128BitsKeyByteArray) * 8); psa_set_key_algorithm(&attr, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT); status = psa_driver_wrapper_aead_encrypt(&attr, key.As(), sizeof(Symmetric128BitsKeyByteArray), PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), Uint8::to_const_uchar(nonce), nonce_length, Uint8::to_const_uchar(aad), aad_length, Uint8::to_const_uchar(plaintext), plaintext_length, allocated_buffer ? buffer : ciphertext, plaintext_length + tag_length, &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == plaintext_length + tag_length, error = CHIP_ERROR_INTERNAL); if (allocated_buffer) { memcpy(Uint8::to_uchar(ciphertext), buffer, plaintext_length); memcpy(Uint8::to_uchar(tag), buffer + plaintext_length, tag_length); memset(buffer, 0, plaintext_length + tag_length); } exit: if (allocated_buffer) { MemoryFree(buffer); } psa_reset_key_attributes(&attr); return error; } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; size_t output_length = 0; uint8_t * buffer = nullptr; bool allocated_buffer = false; VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); // If the ciphertext and tag outputs aren't a contiguous buffer, the PSA API requires buffer copying if (Uint8::to_const_uchar(ciphertext) + ciphertext_len != Uint8::to_const_uchar(tag)) { buffer = (uint8_t *) MemoryCalloc(1, ciphertext_len + tag_length); allocated_buffer = true; VerifyOrExit(buffer != nullptr, error = CHIP_ERROR_NO_MEMORY); } psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_AES); psa_set_key_bits(&attr, sizeof(Symmetric128BitsKeyByteArray) * 8); psa_set_key_algorithm(&attr, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8)); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DECRYPT); if (allocated_buffer) { memcpy(buffer, ciphertext, ciphertext_len); memcpy(buffer + ciphertext_len, tag, tag_length); } status = psa_driver_wrapper_aead_decrypt(&attr, key.As(), sizeof(Symmetric128BitsKeyByteArray), PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length), Uint8::to_const_uchar(nonce), nonce_length, Uint8::to_const_uchar(aad), aad_len, allocated_buffer ? buffer : ciphertext, ciphertext_len + tag_length, plaintext, ciphertext_len, &output_length); if (allocated_buffer) { memset(buffer, 0, ciphertext_len + tag_length); } VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == ciphertext_len, error = CHIP_ERROR_INTERNAL); exit: if (allocated_buffer) { MemoryFree(buffer); } psa_reset_key_attributes(&attr); return error; } CHIP_ERROR Hash_SHA256(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) { size_t output_length = 0; psa_crypto_init(); const psa_status_t result = psa_hash_compute(PSA_ALG_SHA_256, data, data_length, out_buffer, PSA_HASH_LENGTH(PSA_ALG_SHA_256), &output_length); VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL); VerifyOrReturnError(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA1(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) { size_t output_length = 0; psa_crypto_init(); const psa_status_t result = psa_hash_compute(PSA_ALG_SHA_1, data, data_length, out_buffer, PSA_HASH_LENGTH(PSA_ALG_SHA_1), &output_length); VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL); VerifyOrReturnError(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_1), CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } static_assert(kMAX_Hash_SHA256_Context_Size >= sizeof(psa_hash_operation_t), "kMAX_Hash_SHA256_Context_Size is too small for the size of underlying psa_hash_operation_t"); static inline psa_hash_operation_t * to_inner_hash_sha256_context(HashSHA256OpaqueContext * context) { return SafePointerCast(context); } Hash_SHA256_stream::Hash_SHA256_stream(void) { psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); const psa_hash_operation_t initial_context = PSA_HASH_OPERATION_INIT; memcpy(context, &initial_context, sizeof(psa_hash_operation_t)); } Hash_SHA256_stream::~Hash_SHA256_stream(void) { psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); psa_hash_abort(context); Clear(); } CHIP_ERROR Hash_SHA256_stream::Begin(void) { psa_crypto_init(); psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); *context = PSA_HASH_OPERATION_INIT; const psa_status_t result = psa_hash_setup(context, PSA_ALG_SHA_256); VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA256_stream::AddData(const ByteSpan data) { psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); const psa_status_t result = psa_hash_update(context, Uint8::to_const_uchar(data.data()), data.size()); VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA256_stream::GetDigest(MutableByteSpan & out_buffer) { CHIP_ERROR result = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; size_t output_length = 0; psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); // Clone the context first since calculating the digest finishes the operation psa_hash_operation_t digest_context = PSA_HASH_OPERATION_INIT; status = psa_hash_clone(context, &digest_context); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); // Calculate digest on the cloned context status = psa_hash_finish(&digest_context, Uint8::to_uchar(out_buffer.data()), out_buffer.size(), &output_length); VerifyOrExit(status == PSA_SUCCESS, result = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), result = CHIP_ERROR_INTERNAL); exit: psa_hash_abort(&digest_context); return result; } CHIP_ERROR Hash_SHA256_stream::Finish(MutableByteSpan & out_buffer) { psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); size_t output_length = 0; VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); const psa_status_t status = psa_hash_finish(context, Uint8::to_uchar(out_buffer.data()), out_buffer.size(), &output_length); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); VerifyOrReturnError(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } void Hash_SHA256_stream::Clear(void) { psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext); psa_hash_abort(context); } CHIP_ERROR HKDF_sha::HKDF_SHA256(const uint8_t * secret, const size_t secret_length, const uint8_t * salt, const size_t salt_length, const uint8_t * info, const size_t info_length, uint8_t * out_buffer, size_t out_length) { VerifyOrReturnError(secret != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(secret_length > 0, CHIP_ERROR_INVALID_ARGUMENT); // Salt is optional if (salt_length > 0) { VerifyOrReturnError(salt != nullptr, CHIP_ERROR_INVALID_ARGUMENT); } VerifyOrReturnError(info_length > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(info != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_length > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; psa_crypto_init(); status = psa_key_derivation_setup(&operation, PSA_ALG_HKDF(PSA_ALG_SHA_256)); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); if (salt_length > 0) { status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT, Uint8::to_const_uchar(salt), salt_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); } status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SECRET, Uint8::to_const_uchar(secret), secret_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_INFO, Uint8::to_const_uchar(info), info_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); status = psa_key_derivation_output_bytes(&operation, out_buffer, out_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); exit: psa_key_derivation_abort(&operation); return error; } CHIP_ERROR HMAC_sha::HMAC_SHA256(const uint8_t * key, size_t key_length, const uint8_t * message, size_t message_length, uint8_t * out_buffer, size_t out_length) { VerifyOrReturnError(key != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(key_length > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(message != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(message_length > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_length >= kSHA256_Hash_Length, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; size_t output_length = 0; psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC); psa_set_key_bits(&attr, key_length * 8); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_256)); status = psa_driver_wrapper_mac_compute(&attr, Uint8::to_const_uchar(key), key_length, PSA_ALG_HMAC(PSA_ALG_SHA_256), Uint8::to_const_uchar(message), message_length, out_buffer, out_length, &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), error = CHIP_ERROR_INTERNAL); exit: psa_mac_abort(&operation); psa_reset_key_attributes(&attr); return error; } CHIP_ERROR HMAC_sha::HMAC_SHA256(const Hmac128KeyHandle & key, const uint8_t * message, size_t message_length, uint8_t * out_buffer, size_t out_length) { return HMAC_SHA256(key.As(), sizeof(Symmetric128BitsKeyByteArray), message, message_length, out_buffer, out_length); } CHIP_ERROR PBKDF2_sha256::pbkdf2_sha256(const uint8_t * password, size_t plen, const uint8_t * salt, size_t slen, unsigned int iteration_count, uint32_t key_length, uint8_t * output) { // TODO: replace inlined algorithm with usage of the PSA key derivation API once implemented CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; size_t output_length = 0; // Align these buffers on the native data size to speed up the XOR static const size_t hash_size_in_native = ((PSA_HASH_LENGTH(PSA_ALG_SHA_256) + sizeof(unsigned int) - 1) / sizeof(unsigned int)); static_assert(hash_size_in_native * sizeof(unsigned int) >= PSA_HASH_LENGTH(PSA_ALG_SHA_256)); unsigned int md1_buffer[hash_size_in_native]; unsigned int work_buffer[hash_size_in_native]; uint8_t * md1 = (uint8_t *) md1_buffer; uint8_t * work = (uint8_t *) work_buffer; size_t use_len; unsigned char * out_p = output; uint8_t * U1 = (uint8_t *) MemoryCalloc(1, slen + 4); VerifyOrExit(U1 != nullptr, error = CHIP_ERROR_NO_MEMORY); VerifyOrExit(password != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(plen > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(salt != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(slen >= kSpake2p_Min_PBKDF_Salt_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(slen <= kSpake2p_Max_PBKDF_Salt_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(output != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC); psa_set_key_bits(&attr, plen * 8); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_256)); // Start with initializing the salt + counter memcpy(U1, salt, slen); U1[slen] = 0; U1[slen + 1] = 0; U1[slen + 2] = 0; U1[slen + 3] = 1; // Loop until we have generated the requested key length while (key_length) { // U1 ends up in work status = psa_driver_wrapper_mac_compute(&attr, password, plen, PSA_ALG_HMAC(PSA_ALG_SHA_256), U1, slen + 4, work, PSA_HASH_LENGTH(PSA_ALG_SHA_256), &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), error = CHIP_ERROR_INTERNAL); memcpy(md1, work, PSA_HASH_LENGTH(PSA_ALG_SHA_256)); for (size_t i = 1; i < iteration_count; i++) { // U2 ends up in md1 // status = psa_driver_wrapper_mac_compute(&attr, password, plen, PSA_ALG_HMAC(PSA_ALG_SHA_256), md1, sizeof(md1_buffer), md1, sizeof(md1_buffer), &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), error = CHIP_ERROR_INTERNAL); // U1 xor U2 // for (size_t j = 0; j < hash_size_in_native; j++) { work_buffer[j] ^= md1_buffer[j]; } } use_len = (key_length < PSA_HASH_LENGTH(PSA_ALG_SHA_256)) ? key_length : PSA_HASH_LENGTH(PSA_ALG_SHA_256); memcpy(out_p, work, use_len); key_length -= (uint32_t) use_len; out_p += use_len; for (size_t i = 4; i > 0; i--) { if (++U1[slen + i - 1] != 0) { break; } } } exit: MemoryFree(U1); psa_reset_key_attributes(&attr); return error; } CHIP_ERROR add_entropy_source(entropy_source fn_source, void * p_source, size_t threshold) { // PSA Crypto has its own entropy and doesn't support an override mechanism (void) fn_source; (void) p_source; (void) threshold; return CHIP_NO_ERROR; } CHIP_ERROR DRBG_get_bytes(uint8_t * out_buffer, const size_t out_length) { VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_length > 0, CHIP_ERROR_INVALID_ARGUMENT); psa_crypto_init(); const psa_status_t result = psa_generate_random(Uint8::to_uchar(out_buffer), out_length); VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } // CryptoRNG's definition is needed to use the mbedTLS-backed SPAKE2+ and certificate operations static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length) { return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1; } // Mapping function is used as part of the certificate operations mbedtls_ecp_group_id MapECPGroupId(SupportedECPKeyTypes keyType) { switch (keyType) { case SupportedECPKeyTypes::ECP256R1: return MBEDTLS_ECP_DP_SECP256R1; default: return MBEDTLS_ECP_DP_NONE; } } /******************************************************************************* * * WARNING: The default (base) implementation of P256Keypair is UNSAFE! * * Because of the way CHIPCryptoPAL has evolved, it is dictating how a base * P256Keypair should behave. This includes: * * Allowing using a P256 key for both ECDSA and ECDH operations * * Needing to support copying a key through Serialize()/Deserialize() * operations. This can't easily be done opaquely on an opaque backend * without convoluted forms of reference tracking. * * Not including a way to figure out whether the created key is supposed * to be ephemeral or long-lived. * * Needing to support ingestion of specific-format keys through * Deserialize() (to support e.g. the example DAC provider). * * These conditions have lead to the base implementation of this class in this * crypto backend being backed by a plaintext key buffer instead of opaque key * references. Usage of the base class is strongly discouraged, and is only * implemented to support the in-tree examples which instantiate keys of this * base class. * ******************************************************************************/ typedef struct { uint8_t privkey[32]; size_t bitlen; } psa_plaintext_ecp_keypair; static inline psa_plaintext_ecp_keypair * to_keypair(P256KeypairContext * context) { return SafePointerCast(context); } static inline const psa_plaintext_ecp_keypair * to_const_keypair(const P256KeypairContext * context) { return SafePointerCast(context); } CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; size_t output_length = 0; const psa_plaintext_ecp_keypair * keypair = to_const_keypair(&mKeypair); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); VerifyOrExit((msg != nullptr) && (msg_length > 0), error = CHIP_ERROR_INVALID_ARGUMENT); psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, keypair->bitlen); psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE); // use imported key to sign a message status = psa_driver_wrapper_sign_message(&attr, keypair->privkey, PSA_BITS_TO_BYTES(keypair->bitlen), PSA_ALG_ECDSA(PSA_ALG_SHA_256), msg, msg_length, out_signature.Bytes(), out_signature.Capacity(), &output_length); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); VerifyOrReturnError(output_length == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INTERNAL); VerifyOrReturnError(out_signature.SetLength(output_length) == CHIP_NO_ERROR, CHIP_ERROR_INTERNAL); exit: _log_PSA_error(status); psa_reset_key_attributes(&attr); return error; } CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const P256ECDSASignature & signature) const { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; VerifyOrExit((msg != nullptr) && (msg_length > 0), error = CHIP_ERROR_INVALID_ARGUMENT); psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, 256); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_VERIFY_MESSAGE); psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); // use imported key to verify a message status = psa_driver_wrapper_verify_message(&attr, Uint8::to_const_uchar(*this), Length(), PSA_ALG_ECDSA(PSA_ALG_SHA_256), msg, msg_length, signature.ConstBytes(), signature.Length()); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INVALID_SIGNATURE); exit: _log_PSA_error(status); psa_reset_key_attributes(&attr); return error; } CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, const P256ECDSASignature & signature) const { VerifyOrReturnError(hash != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(hash_length == kSHA256_Hash_Length, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(signature.Length() == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INVALID_ARGUMENT); CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; // Step 1: import public key as volatile psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, 256); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_VERIFY_HASH); psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); // use imported key to verify a hash status = psa_driver_wrapper_verify_hash(&attr, Uint8::to_const_uchar(*this), Length(), PSA_ALG_ECDSA(PSA_ALG_SHA_256), hash, hash_length, signature.ConstBytes(), signature.Length()); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INVALID_SIGNATURE); exit: _log_PSA_error(status); psa_reset_key_attributes(&attr); return error; } CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { // Todo: replace with driver call once key derivation through the driver wrapper has been figured out CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; mbedtls_svc_key_id_t key_id = 0; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; size_t output_length = 0; const psa_plaintext_ecp_keypair * keypair = to_const_keypair(&mKeypair); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); // Step 1: import plaintext key as volatile for ECDH psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, keypair->bitlen); psa_set_key_algorithm(&attr, PSA_ALG_ECDH); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DERIVE); status = psa_import_key(&attr, keypair->privkey, PSA_BITS_TO_BYTES(keypair->bitlen), &key_id); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); // Step 2: do key derivation status = psa_raw_key_agreement(PSA_ALG_ECDH, key_id, Uint8::to_const_uchar(remote_public_key), remote_public_key.Length(), out_secret.Bytes(), (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(), &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); SuccessOrExit(error = out_secret.SetLength(output_length)); exit: _log_PSA_error(status); // Step 3: destroy imported key psa_reset_key_attributes(&attr); if (key_id != 0) { psa_destroy_key(key_id); } return error; } void ClearSecretData(uint8_t * buf, size_t len) { mbedtls_platform_zeroize(buf, len); } // THE BELOW IS FROM `third_party/openthread/repo/third_party/mbedtls/repo/library/constant_time.c` since // mbedtls_ct_memcmp is not available on Linux somehow :( int mbedtls_ct_memcmp_copy(const void * a, const void * b, size_t n) { size_t i; volatile const unsigned char * A = (volatile const unsigned char *) a; volatile const unsigned char * B = (volatile const unsigned char *) b; volatile unsigned char diff = 0; for (i = 0; i < n; i++) { /* Read volatile data in order before computing diff. * This avoids IAR compiler warning: * 'the order of volatile accesses is undefined ..' */ unsigned char x = A[i], y = B[i]; diff |= x ^ y; } return ((int) diff); } bool IsBufferContentEqualConstantTime(const void * a, const void * b, size_t n) { return mbedtls_ct_memcmp_copy(a, b, n) == 0; } CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_plaintext_ecp_keypair * keypair = to_keypair(&mKeypair); size_t output_length; if (mInitialized) { return CHIP_ERROR_INCORRECT_STATE; } // Step 1: Generate a volatile new key psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; psa_crypto_init(); psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, 256); psa_set_key_algorithm(&attr, PSA_ALG_ECDH); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); status = psa_driver_wrapper_generate_key(&attr, keypair->privkey, sizeof(keypair->privkey), &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == kP256_PrivateKey_Length, error = CHIP_ERROR_INTERNAL); keypair->bitlen = 256; // Step 2: Export the public key into the pubkey member status = psa_driver_wrapper_export_public_key(&attr, keypair->privkey, sizeof(keypair->privkey), Uint8::to_uchar(mPublicKey), mPublicKey.Length(), &output_length); VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); VerifyOrExit(output_length == kP256_PublicKey_Length, error = CHIP_ERROR_INTERNAL); exit: _log_PSA_error(status); if (error == CHIP_NO_ERROR) { mInitialized = true; } psa_reset_key_attributes(&attr); return error; } CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const { CHIP_ERROR error = CHIP_NO_ERROR; const psa_plaintext_ecp_keypair * keypair = to_const_keypair(&mKeypair); size_t len = output.Length() == 0 ? output.Capacity() : output.Length(); Encoding::BufferWriter bbuf(output.Bytes(), len); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); bbuf.Put(mPublicKey, mPublicKey.Length()); VerifyOrExit(bbuf.Available() == sizeof(keypair->privkey), error = CHIP_ERROR_INTERNAL); bbuf.Put(keypair->privkey, PSA_BITS_TO_BYTES(keypair->bitlen)); VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_BUFFER_TOO_SMALL); output.SetLength(bbuf.Needed()); exit: return error; } CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) { CHIP_ERROR error = CHIP_NO_ERROR; psa_plaintext_ecp_keypair * keypair = to_keypair(&mKeypair); Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length()); VerifyOrExit(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, error = CHIP_ERROR_INVALID_ARGUMENT); Clear(); memcpy(keypair->privkey, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length); keypair->bitlen = 256; bbuf.Put(input.ConstBytes(), mPublicKey.Length()); VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); mInitialized = true; exit: return error; } void P256Keypair::Clear() { if (mInitialized) { psa_plaintext_ecp_keypair * keypair = to_keypair(&mKeypair); memset(keypair, 0, sizeof(psa_plaintext_ecp_keypair)); mInitialized = false; } } P256Keypair::~P256Keypair() { Clear(); } CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & csr_length) const { MutableByteSpan csr(out_csr, csr_length); CHIP_ERROR err = GenerateCertificateSigningRequest(this, csr); csr_length = (CHIP_NO_ERROR == err) ? csr.size() : 0; return err; } CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr_buf, size_t csr_length, P256PublicKey & pubkey) { #if defined(MBEDTLS_X509_CSR_PARSE_C) ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr_buf, csr_length)); // TODO: For some embedded targets, mbedTLS library doesn't have mbedtls_x509_csr_parse_der, and mbedtls_x509_csr_parse_free. // Taking a step back, embedded targets likely will not process CSR requests. Adding this action item to reevaluate // this if there's a need for this processing for embedded targets. CHIP_ERROR error = CHIP_NO_ERROR; size_t pubkey_size = 0; mbedtls_ecp_keypair * keypair = nullptr; P256ECDSASignature signature; MutableByteSpan out_raw_sig_span(signature.Bytes(), signature.Capacity()); mbedtls_x509_csr csr; mbedtls_x509_csr_init(&csr); int result = mbedtls_x509_csr_parse_der(&csr, csr_buf, csr_length); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Verify the signature algorithm and public key type VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_md) == MBEDTLS_MD_SHA256, error = CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_pk) == MBEDTLS_PK_ECDSA, error = CHIP_ERROR_WRONG_KEY_TYPE); keypair = mbedtls_pk_ec(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); // Copy the public key from the CSR result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey), pubkey.Length()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); // Convert DER signature to raw signature error = EcdsaAsn1SignatureToRaw(kP256_FE_Length, ByteSpan{ csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(p), csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(len) }, out_raw_sig_span); VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(out_raw_sig_span.size() == (kP256_FE_Length * 2), error = CHIP_ERROR_INTERNAL); signature.SetLength(out_raw_sig_span.size()); // Verify the signature using the public key error = pubkey.ECDSA_validate_msg_signature(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(p), csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(len), signature); SuccessOrExit(error); exit: mbedtls_x509_csr_free(&csr); _log_mbedTLS_error(result); return error; #else ChipLogError(Crypto, "MBEDTLS_X509_CSR_PARSE_C is not enabled. CSR cannot be parsed"); return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; #endif } typedef struct Spake2p_Context { mbedtls_ecp_group curve; mbedtls_ecp_point M; mbedtls_ecp_point N; mbedtls_ecp_point X; mbedtls_ecp_point Y; mbedtls_ecp_point L; mbedtls_ecp_point Z; mbedtls_ecp_point V; mbedtls_mpi w0; mbedtls_mpi w1; mbedtls_mpi xy; mbedtls_mpi tempbn; } Spake2p_Context; static inline Spake2p_Context * to_inner_spake2p_context(Spake2pOpaqueContext * context) { return SafePointerCast(context); } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitInternal(void) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); memset(context, 0, sizeof(Spake2p_Context)); mbedtls_ecp_group_init(&context->curve); result = mbedtls_ecp_group_load(&context->curve, MBEDTLS_ECP_DP_SECP256R1); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256) != nullptr, error = CHIP_ERROR_INTERNAL); mbedtls_ecp_point_init(&context->M); mbedtls_ecp_point_init(&context->N); mbedtls_ecp_point_init(&context->X); mbedtls_ecp_point_init(&context->Y); mbedtls_ecp_point_init(&context->L); mbedtls_ecp_point_init(&context->V); mbedtls_ecp_point_init(&context->Z); M = &context->M; N = &context->N; X = &context->X; Y = &context->Y; L = &context->L; V = &context->V; Z = &context->Z; mbedtls_mpi_init(&context->w0); mbedtls_mpi_init(&context->w1); mbedtls_mpi_init(&context->xy); mbedtls_mpi_init(&context->tempbn); w0 = &context->w0; w1 = &context->w1; xy = &context->xy; tempbn = &context->tempbn; G = &context->curve.G; order = &context->curve.N; return error; exit: _log_mbedTLS_error(result); Clear(); return error; } void Spake2p_P256_SHA256_HKDF_HMAC::Clear() { VerifyOrReturn(state != CHIP_SPAKE2P_STATE::PREINIT); Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); mbedtls_ecp_point_free(&context->M); mbedtls_ecp_point_free(&context->N); mbedtls_ecp_point_free(&context->X); mbedtls_ecp_point_free(&context->Y); mbedtls_ecp_point_free(&context->L); mbedtls_ecp_point_free(&context->Z); mbedtls_ecp_point_free(&context->V); mbedtls_mpi_free(&context->w0); mbedtls_mpi_free(&context->w1); mbedtls_mpi_free(&context->xy); mbedtls_mpi_free(&context->tempbn); mbedtls_ecp_group_free(&context->curve); state = CHIP_SPAKE2P_STATE::PREINIT; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, MutableByteSpan & out_span) { HMAC_sha hmac; VerifyOrReturnError(out_span.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); ReturnErrorOnFailure(hmac.HMAC_SHA256(key, key_len, in, in_len, out_span.data(), kSHA256_Hash_Length)); out_span = out_span.SubSpan(0, kSHA256_Hash_Length); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, const uint8_t * in, size_t in_len) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; uint8_t computed_mac[kSHA256_Hash_Length]; MutableByteSpan computed_mac_span{ computed_mac }; VerifyOrExit(mac_len == kSHA256_Hash_Length, error = CHIP_ERROR_INVALID_ARGUMENT); SuccessOrExit(error = Mac(key, key_len, in, in_len, computed_mac_span)); VerifyOrExit(computed_mac_span.size() == mac_len, error = CHIP_ERROR_INTERNAL); VerifyOrExit(IsBufferContentEqualConstantTime(mac, computed_mac, kSHA256_Hash_Length), error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FELoad(const uint8_t * in, size_t in_len, void * fe) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; result = mbedtls_mpi_read_binary((mbedtls_mpi *) fe, Uint8::to_const_uchar(in), in_len); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); result = mbedtls_mpi_mod_mpi((mbedtls_mpi *) fe, (mbedtls_mpi *) fe, (const mbedtls_mpi *) order); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEWrite(const void * fe, uint8_t * out, size_t out_len) { if (mbedtls_mpi_write_binary((const mbedtls_mpi *) fe, Uint8::to_uchar(out), out_len) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEGenerate(void * fe) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); result = mbedtls_ecp_gen_privkey(&context->curve, (mbedtls_mpi *) fe, CryptoRNG, nullptr); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEMul(void * fer, const void * fe1, const void * fe2) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; result = mbedtls_mpi_mul_mpi((mbedtls_mpi *) fer, (const mbedtls_mpi *) fe1, (const mbedtls_mpi *) fe2); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); result = mbedtls_mpi_mod_mpi((mbedtls_mpi *) fer, (mbedtls_mpi *) fer, (const mbedtls_mpi *) order); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointLoad(const uint8_t * in, size_t in_len, void * R) { Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); if (mbedtls_ecp_point_read_binary(&context->curve, (mbedtls_ecp_point *) R, Uint8::to_const_uchar(in), in_len) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointWrite(const void * R, uint8_t * out, size_t out_len) { memset(out, 0, out_len); size_t mbedtls_out_len = out_len; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); if (mbedtls_ecp_point_write_binary(&context->curve, (const mbedtls_ecp_point *) R, MBEDTLS_ECP_PF_UNCOMPRESSED, &mbedtls_out_len, Uint8::to_uchar(out), out_len) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } extern "C" { #include "em_device.h" } #if defined(SEMAILBOX_PRESENT) // Add inlined optimisation which can use the SE to do point multiplication operations using // the ECDH primitive as a proxy for scalar multiplication. extern "C" { #include "sl_se_manager.h" #include "sl_se_manager_key_derivation.h" #include "sl_se_manager_util.h" #include "sli_se_driver_key_management.h" #include "sli_se_manager_internal.h" } #endif /* SEMAILBOX_PRESENT */ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointMul(void * R, const void * P1, const void * fe1) { Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); #if defined(SEMAILBOX_PRESENT) psa_status_t status = PSA_SUCCESS; uint8_t point[2 * kP256_FE_Length] = { 0 }; uint8_t scalar[kP256_FE_Length] = { 0 }; // This inlined implementation only supports P256, but check assumptions if (context->curve.id != MBEDTLS_ECP_DP_SECP256R1) { return CHIP_ERROR_INVALID_ARGUMENT; } /* pull out key info from mbedtls structures */ status = mbedtls_mpi_write_binary((const mbedtls_mpi *) fe1, scalar, sizeof(scalar)); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } status = mbedtls_mpi_write_binary(&((const mbedtls_ecp_point *) P1)->MBEDTLS_PRIVATE(X), point, kP256_FE_Length); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } status = mbedtls_mpi_write_binary(&((const mbedtls_ecp_point *) P1)->MBEDTLS_PRIVATE(Y), point + kP256_FE_Length, kP256_FE_Length); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } { sl_se_key_descriptor_t priv_desc = { 0 }; sl_se_key_descriptor_t pub_desc = { 0 }; sl_se_key_descriptor_t shared_desc = { 0 }; sl_se_command_context_t cmd_ctx = SL_SE_COMMAND_CONTEXT_INIT; sl_status_t sl_status = SL_STATUS_FAIL; // Set private key to scalar priv_desc.type = SL_SE_KEY_TYPE_ECC_P256; priv_desc.flags |= SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY; sli_se_key_descriptor_set_plaintext(&priv_desc, scalar, sizeof(scalar)); // Set public key to point pub_desc.type = SL_SE_KEY_TYPE_ECC_P256; pub_desc.flags |= SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PUBLIC_KEY; sli_se_key_descriptor_set_plaintext(&pub_desc, point, sizeof(point)); // Set output to point shared_desc.type = SL_SE_KEY_TYPE_SYMMETRIC; shared_desc.size = sizeof(point); sli_se_key_descriptor_set_plaintext(&shared_desc, point, sizeof(point)); // Re-init SE command context. sl_status = sl_se_init_command_context(&cmd_ctx); if (sl_status != SL_STATUS_OK) { return CHIP_ERROR_INTERNAL; } // Perform key agreement algorithm (ECDH). sl_status = sl_se_ecdh_compute_shared_secret(&cmd_ctx, &priv_desc, &pub_desc, &shared_desc); if (sl_status != SL_STATUS_OK) { ChipLogError(Crypto, "ECDH SL failure %lx", sl_status); if (sl_status == SL_STATUS_COMMAND_IS_INVALID) { // This error will be returned if the key type isn't supported. return CHIP_ERROR_NOT_IMPLEMENTED; } else { // If the ECDH operation failed, this is most likely due to the peer key // being an invalid elliptic curve point. Other sources for failure should // hopefully have been caught during parameter validation. return CHIP_ERROR_INVALID_ARGUMENT; } } } status = mbedtls_mpi_read_binary(&((mbedtls_ecp_point *) R)->MBEDTLS_PRIVATE(X), point, kP256_FE_Length); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } status = mbedtls_mpi_read_binary(&((mbedtls_ecp_point *) R)->MBEDTLS_PRIVATE(Y), point + kP256_FE_Length, kP256_FE_Length); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } status = mbedtls_mpi_lset(&((mbedtls_ecp_point *) R)->MBEDTLS_PRIVATE(Z), 1); if (status != PSA_SUCCESS) { return CHIP_ERROR_INTERNAL; } #else /* SEMAILBOX_PRESENT */ if (mbedtls_ecp_mul(&context->curve, (mbedtls_ecp_point *) R, (const mbedtls_mpi *) fe1, (const mbedtls_ecp_point *) P1, CryptoRNG, nullptr) != 0) { return CHIP_ERROR_INTERNAL; } #endif /* SEMAILBOX_PRESENT */ return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, const void * fe2) { Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); #if defined(SEMAILBOX_PRESENT) CHIP_ERROR error = CHIP_NO_ERROR; int result; // Accelerate 'muladd' using separate point multiplication operations mbedtls_ecp_point fe1P1, fe2P2; mbedtls_mpi one; mbedtls_mpi_init(&one); mbedtls_ecp_point_init(&fe1P1); mbedtls_ecp_point_init(&fe2P2); result = mbedtls_mpi_lset(&one, 1); VerifyOrExit(result == 0, error = CHIP_ERROR_NO_MEMORY); // Do fe1P1 = fe1 * P1 and fe2P2 = fe2 * P2 since those can be accelerated SuccessOrExit(error = PointMul(&fe1P1, P1, fe1)); SuccessOrExit(error = PointMul(&fe2P2, P2, fe2)); // Do R = (1 * fe1P1) + (1 * fe2P2) since point addition is not a public mbedTLS API // mbedTLS will apply a shortcut since (1 * A) == A result = mbedtls_ecp_muladd(&context->curve, (mbedtls_ecp_point *) R, &one, &fe1P1, &one, &fe2P2); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: mbedtls_mpi_free(&one); mbedtls_ecp_point_free(&fe1P1); mbedtls_ecp_point_free(&fe2P2); return error; #else /* SEMAILBOX_PRESENT */ if (mbedtls_ecp_muladd(&context->curve, (mbedtls_ecp_point *) R, (const mbedtls_mpi *) fe1, (const mbedtls_ecp_point *) P1, (const mbedtls_mpi *) fe2, (const mbedtls_ecp_point *) P2) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; #endif /* SEMAILBOX_PRESENT */ } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointInvert(void * R) { mbedtls_ecp_point * Rp = (mbedtls_ecp_point *) R; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); if (mbedtls_mpi_sub_mpi(&Rp->CHIP_CRYPTO_PAL_PRIVATE(Y), &context->curve.P, &Rp->CHIP_CRYPTO_PAL_PRIVATE(Y)) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointCofactorMul(void * R) { return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); mbedtls_mpi w1_bn; mbedtls_ecp_point Ltemp; mbedtls_mpi_init(&w1_bn); mbedtls_ecp_point_init(&Ltemp); result = mbedtls_mpi_read_binary(&w1_bn, Uint8::to_const_uchar(w1in), w1in_len); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); result = mbedtls_mpi_mod_mpi(&w1_bn, &w1_bn, &context->curve.N); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); #if defined(SEMAILBOX_PRESENT) // Do the point multiplication using hardware acceleration via ECDH primitive error = PointMul(&Ltemp, &context->curve.G, &w1_bn); if (error != CHIP_NO_ERROR) { goto exit; } #else /* SEMAILBOX_PRESENT */ result = mbedtls_ecp_mul(&context->curve, &Ltemp, &w1_bn, &context->curve.G, CryptoRNG, nullptr); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); #endif /* SEMAILBOX_PRESENT */ memset(Lout, 0, *L_len); result = mbedtls_ecp_point_write_binary(&context->curve, &Ltemp, MBEDTLS_ECP_PF_UNCOMPRESSED, L_len, Uint8::to_uchar(Lout), *L_len); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); mbedtls_ecp_point_free(&Ltemp); mbedtls_mpi_free(&w1_bn); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointIsValid(void * R) { Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); if (mbedtls_ecp_check_pubkey(&context->curve, (mbedtls_ecp_point *) R) != 0) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } constexpr uint8_t sOID_AttributeType_CommonName[] = { 0x55, 0x04, 0x03 }; constexpr uint8_t sOID_AttributeType_MatterVendorId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x01 }; constexpr uint8_t sOID_AttributeType_MatterProductId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x02 }; constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; constexpr uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 }; constexpr uint8_t sOID_Extension_KeyUsage[] = { 0x55, 0x1D, 0x0F }; constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; constexpr uint8_t sOID_Extension_CRLDistributionPoint[] = { 0x55, 0x1D, 0x1F }; /** * Compares an mbedtls_asn1_buf structure (oidBuf) to a reference OID represented as uint8_t array (oid). */ #define OID_CMP(oid, oidBuf) \ ((MBEDTLS_ASN1_OID == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(tag)) && \ (sizeof(oid) == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) && \ (memcmp((oid), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(p), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) == 0)) CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; mbedtls_x509_crt mbed_cert; unsigned char * p = nullptr; const unsigned char * end = nullptr; size_t len = 0; bool extBasicPresent = false; bool extKeyUsagePresent = false; VerifyOrReturnError(!cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbed_cert); result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(cert.data()), cert.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // "version" value is 1 higher than the actual encoded value. VerifyOrExit(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(version) - 1 == 2, error = CHIP_ERROR_INTERNAL); // Verify signature algorithms is ECDSA with SHA256. VerifyOrExit(OID_CMP(sOID_SigAlgo_ECDSAWithSHA256, mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(sig_oid)), error = CHIP_ERROR_INTERNAL); // Verify public key presence and format. { Crypto::P256PublicKey pubkey; SuccessOrExit(error = ExtractPubkeyFromX509Cert(cert, pubkey)); } p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); end = p + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); while (p < end) { mbedtls_x509_buf extOID = { 0, 0, nullptr }; int extCritical = 0; result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); /* Get extension ID */ result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(tag) = MBEDTLS_ASN1_OID; extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(len) = len; extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(p) = p; p += len; /* Get optional critical */ result = mbedtls_asn1_get_bool(&p, end, &extCritical); VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); /* Data should be octet string type */ result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); if (OID_CMP(sOID_Extension_BasicConstraints, extOID)) { int isCA = 0; int pathLen = -1; VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); extBasicPresent = true; result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); if (len > 0) { unsigned char * seqStart = p; result = mbedtls_asn1_get_bool(&p, end, &isCA); VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); // Check if pathLen is there by validating if the cursor didn't get to the end of // of the internal SEQUENCE for the basic constraints encapsulation. // Missing pathLen optional tag will leave pathLen == -1 for following checks. bool hasPathLen = (p != (seqStart + len)); if (hasPathLen) { // Extract pathLen value, making sure it's a valid format. result = mbedtls_asn1_get_int(&p, end, &pathLen); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); } } if (certType == AttestationCertType::kDAC) { VerifyOrExit(!isCA && pathLen == -1, error = CHIP_ERROR_INTERNAL); } else if (certType == AttestationCertType::kPAI) { VerifyOrExit(isCA && pathLen == 0, error = CHIP_ERROR_INTERNAL); } else { // For PAA, pathlen must be absent or equal to 1 (see Matter 1.1 spec 6.2.2.5) VerifyOrExit(isCA && (pathLen == -1 || pathLen == 1), error = CHIP_ERROR_INTERNAL); } } else if (OID_CMP(sOID_Extension_KeyUsage, extOID)) { mbedtls_x509_bitstring bs = { 0, 0, nullptr }; unsigned int keyUsage = 0; VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); extKeyUsagePresent = true; result = mbedtls_asn1_get_bitstring(&p, p + len, &bs); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); for (size_t i = 0; i < bs.CHIP_CRYPTO_PAL_PRIVATE_X509(len) && i < sizeof(unsigned int); i++) { keyUsage |= static_cast(bs.CHIP_CRYPTO_PAL_PRIVATE_X509(p)[i]) << (8 * i); } if (certType == AttestationCertType::kDAC) { // SHALL only have the digitalSignature bit set. VerifyOrExit(keyUsage == MBEDTLS_X509_KU_DIGITAL_SIGNATURE, error = CHIP_ERROR_INTERNAL); } else { bool keyCertSignFlag = keyUsage & MBEDTLS_X509_KU_KEY_CERT_SIGN; bool crlSignFlag = keyUsage & MBEDTLS_X509_KU_CRL_SIGN; bool otherFlags = keyUsage & ~(MBEDTLS_X509_KU_CRL_SIGN | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_DIGITAL_SIGNATURE); VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, error = CHIP_ERROR_INTERNAL); } } else { p += len; } } // Verify basic and key usage extensions are present. VerifyOrExit(extBasicPresent && extKeyUsagePresent, error = CHIP_ERROR_INTERNAL); // Verify that SKID and AKID extensions are present. { uint8_t kidBuf[kSubjectKeyIdentifierLength]; MutableByteSpan kid(kidBuf); SuccessOrExit(error = ExtractSKIDFromX509Cert(cert, kid)); if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) { // Mandatory extension for DAC and PAI certs. SuccessOrExit(error = ExtractAKIDFromX509Cert(cert, kid)); } } exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) cert; (void) certType; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, CertificateChainValidationResult & result) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; mbedtls_x509_crt certChain; mbedtls_x509_crt rootCert; mbedtls_x509_time leaf_valid_from; mbedtls_x509_crt * cert = NULL; int mbedResult; uint32_t flags; int compare_from = 0; int compare_until = 0; result = CertificateChainValidationResult::kInternalFrameworkError; VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0, (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0, (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); mbedtls_x509_crt_init(&certChain); mbedtls_x509_crt_init(&rootCert); /* Start of chain */ mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen); VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL)); leaf_valid_from = certChain.valid_from; /* Add the intermediate to the chain, if present */ if (caCertificate != nullptr && caCertificateLen > 0) { mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen); VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kICAFormatInvalid, error = CHIP_ERROR_INTERNAL)); } /* Parse the root cert */ mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen); VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL)); /* Validates that intermediate and root certificates are valid at the time of the leaf certificate's start time. */ compare_from = timeCompare(&leaf_valid_from, &rootCert.valid_from); compare_until = timeCompare(&leaf_valid_from, &rootCert.valid_to); VerifyOrExit((compare_from >= 0) && (compare_until <= 0), (result = CertificateChainValidationResult::kChainInvalid, error = CHIP_ERROR_CERT_NOT_TRUSTED)); cert = certChain.next; while (cert) { compare_from = timeCompare(&leaf_valid_from, &cert->valid_from); compare_until = timeCompare(&leaf_valid_from, &cert->valid_to); VerifyOrExit((compare_from >= 0) && (compare_until <= 0), (result = CertificateChainValidationResult::kChainInvalid, error = CHIP_ERROR_CERT_NOT_TRUSTED)); cert = cert->next; } /* Verify the chain against the root */ mbedResult = mbedtls_x509_crt_verify(&certChain, &rootCert, NULL, NULL, &flags, NULL, NULL); switch (mbedResult) { case 0: VerifyOrExit(flags == 0, (result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); result = CertificateChainValidationResult::kSuccess; break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: result = CertificateChainValidationResult::kChainInvalid; error = CHIP_ERROR_CERT_NOT_TRUSTED; break; default: result = CertificateChainValidationResult::kInternalFrameworkError; error = CHIP_ERROR_INTERNAL; break; } exit: _log_mbedTLS_error(mbedResult); mbedtls_x509_crt_free(&certChain); mbedtls_x509_crt_free(&rootCert); #else (void) rootCertificate; (void) rootCertificateLen; (void) caCertificate; (void) caCertificateLen; (void) leafCertificate; (void) leafCertificateLen; (void) result; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } inline bool IsTimeGreaterThanEqual(const mbedtls_x509_time * const timeA, const mbedtls_x509_time * const timeB) { // checks if two values are different and if yes, then returns first > second. #define RETURN_STRICTLY_GREATER_IF_DIFFERENT(component) \ { \ auto valueA = timeA->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ auto valueB = timeB->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ \ if (valueA != valueB) \ { \ return valueA > valueB; \ } \ } RETURN_STRICTLY_GREATER_IF_DIFFERENT(year); RETURN_STRICTLY_GREATER_IF_DIFFERENT(mon); RETURN_STRICTLY_GREATER_IF_DIFFERENT(day); RETURN_STRICTLY_GREATER_IF_DIFFERENT(hour); RETURN_STRICTLY_GREATER_IF_DIFFERENT(min); RETURN_STRICTLY_GREATER_IF_DIFFERENT(sec); // all above are equal return true; } CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & referenceCertificate, const ByteSpan & toBeEvaluatedCertificate) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; mbedtls_x509_crt mbedReferenceCertificate; mbedtls_x509_crt mbedToBeEvaluatedCertificate; mbedtls_x509_time refNotBeforeTime; mbedtls_x509_time tbeNotBeforeTime; mbedtls_x509_time tbeNotAfterTime; int result; VerifyOrReturnError(!referenceCertificate.empty() && !toBeEvaluatedCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbedReferenceCertificate); mbedtls_x509_crt_init(&mbedToBeEvaluatedCertificate); result = mbedtls_x509_crt_parse(&mbedReferenceCertificate, Uint8::to_const_uchar(referenceCertificate.data()), referenceCertificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); result = mbedtls_x509_crt_parse(&mbedToBeEvaluatedCertificate, Uint8::to_const_uchar(toBeEvaluatedCertificate.data()), toBeEvaluatedCertificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); refNotBeforeTime = mbedReferenceCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); tbeNotBeforeTime = mbedToBeEvaluatedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); tbeNotAfterTime = mbedToBeEvaluatedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to); // check if referenceCertificate is issued at or after tbeCertificate's notBefore timestamp VerifyOrExit(IsTimeGreaterThanEqual(&refNotBeforeTime, &tbeNotBeforeTime), error = CHIP_ERROR_CERT_EXPIRED); // check if referenceCertificate is issued at or before tbeCertificate's notAfter timestamp VerifyOrExit(IsTimeGreaterThanEqual(&tbeNotAfterTime, &refNotBeforeTime), error = CHIP_ERROR_CERT_EXPIRED); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbedReferenceCertificate); mbedtls_x509_crt_free(&mbedToBeEvaluatedCertificate); #else (void) referenceCertificate; (void) toBeEvaluatedCertificate; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; mbedtls_x509_crt mbedCertificate; int result; VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbedCertificate); result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // check if certificate's notBefore timestamp is earlier than or equal to current time. result = mbedtls_x509_time_is_past(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from)); VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); // check if certificate's notAfter timestamp is later than current time. result = mbedtls_x509_time_is_future(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to)); VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbedCertificate); #else (void) certificate; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; mbedtls_x509_crt mbed_cert; mbedtls_ecp_keypair * keypair = nullptr; size_t pubkey_size = 0; mbedtls_x509_crt_init(&mbed_cert); int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit(mbedtls_pk_get_type(&(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk))) == MBEDTLS_PK_ECKEY, error = CHIP_ERROR_INVALID_ARGUMENT); keypair = mbedtls_pk_ec(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); VerifyOrExit(keypair->CHIP_CRYPTO_PAL_PRIVATE(grp).id == MapECPGroupId(pubkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); // Copy the public key from the cert in raw point format result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey.Bytes()), pubkey.Length()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) pubkey; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } namespace { CHIP_ERROR ExtractKIDFromX509Cert(bool extractSKID, const ByteSpan & certificate, MutableByteSpan & kid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; mbedtls_x509_crt mbed_cert; unsigned char * p = nullptr; const unsigned char * end = nullptr; size_t len = 0; mbedtls_x509_crt_init(&mbed_cert); int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // TODO: The mbedTLS team is working on supporting SKID and AKID extensions processing. // Once it is supported, this code should be updated. p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); while (p < end) { result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; bool extractCurrentExtSKID = extractSKID && OID_CMP(sOID_Extension_SubjectKeyIdentifier, extOID); bool extractCurrentExtAKID = !extractSKID && OID_CMP(sOID_Extension_AuthorityKeyIdentifier, extOID); p += len; int is_critical = 0; result = mbedtls_asn1_get_bool(&p, end, &is_critical); VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); if (extractCurrentExtSKID || extractCurrentExtAKID) { if (extractCurrentExtSKID) { result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); } else { result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); // Other optional fields, authorityCertIssuer and authorityCertSerialNumber, // will be skipped if present. } VerifyOrExit(len == kSubjectKeyIdentifierLength, error = CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrExit(len <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(kid.data(), p, len); if (kid.size() > len) { kid.reduce_size(len); } ExitNow(error = CHIP_NO_ERROR); break; } p += len; } exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) kid; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } } // namespace CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid) { return ExtractKIDFromX509Cert(true, certificate, skid); } CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) { return ExtractKIDFromX509Cert(false, certificate, akid); } CHIP_ERROR ExtractCRLDistributionPointURIFromX509Cert(const ByteSpan & certificate, MutableCharSpan & cdpurl) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; mbedtls_x509_crt mbed_cert; unsigned char * p = nullptr; const unsigned char * end = nullptr; size_t len = 0; size_t cdpExtCount = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbed_cert); int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); while (p < end) { result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); p += len; int is_critical = 0; result = mbedtls_asn1_get_bool(&p, end, &is_critical); VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); unsigned char * end_of_ext = p + len; if (isCurrentExtCDP) { // Only one CRL Distribution Point Extension is allowed. cdpExtCount++; VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint // // This implementation only supports a single DistributionPoint (sequence of size 1), // which is verified by comparing (p + len == end_of_ext) result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); // The DistributionPoint is a sequence of three optional elements: // DistributionPoint ::= SEQUENCE { // distributionPoint [0] DistributionPointName OPTIONAL, // reasons [1] ReasonFlags OPTIONAL, // cRLIssuer [2] GeneralNames OPTIONAL } result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); // The DistributionPointName is: // DistributionPointName ::= CHOICE { // fullName [0] GeneralNames, // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } // // The URI should be encoded in the fullName element. result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); unsigned char * end_of_general_names = p + len; // The CDP URI is encoded as a uniformResourceIdentifier field of the GeneralName: // GeneralName ::= CHOICE { // otherName [0] OtherName, // rfc822Name [1] IA5String, // dNSName [2] IA5String, // x400Address [3] ORAddress, // directoryName [4] Name, // ediPartyName [5] EDIPartyName, // uniformResourceIdentifier [6] IA5String, // iPAddress [7] OCTET STRING, // registeredID [8] OBJECT IDENTIFIER } result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); // Only single URI instance in the GeneralNames is supported VerifyOrExit(p + len == end_of_general_names, error = CHIP_ERROR_NOT_FOUND); const char * urlptr = reinterpret_cast(p); VerifyOrExit((len > strlen(kValidCDPURIHttpPrefix) && strncmp(urlptr, kValidCDPURIHttpPrefix, strlen(kValidCDPURIHttpPrefix)) == 0) || (len > strlen(kValidCDPURIHttpsPrefix) && strncmp(urlptr, kValidCDPURIHttpsPrefix, strlen(kValidCDPURIHttpsPrefix)) == 0), error = CHIP_ERROR_NOT_FOUND); error = CopyCharSpanToMutableCharSpan(CharSpan(urlptr, len), cdpurl); SuccessOrExit(error); } p = end_of_ext; } VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) cdpurl; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR ExtractCDPExtensionCRLIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & crlIssuer) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; mbedtls_x509_crt mbed_cert; unsigned char * p = nullptr; const unsigned char * end = nullptr; size_t len = 0; size_t cdpExtCount = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbed_cert); int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); while (p < end) { result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); p += len; int is_critical = 0; result = mbedtls_asn1_get_bool(&p, end, &is_critical); VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); unsigned char * end_of_ext = p + len; if (isCurrentExtCDP) { // Only one CRL Distribution Point Extension is allowed. cdpExtCount++; VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint // // This implementation only supports a single DistributionPoint (sequence of size 1), // which is verified by comparing (p + len == end_of_ext) result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); // The DistributionPoint is a sequence of three optional elements: // DistributionPoint ::= SEQUENCE { // distributionPoint [0] DistributionPointName OPTIONAL, // reasons [1] ReasonFlags OPTIONAL, // cRLIssuer [2] GeneralNames OPTIONAL } result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); // If distributionPoint element presents, ignore it result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); if (result == 0) { p += len; VerifyOrExit(p < end_of_ext, error = CHIP_ERROR_NOT_FOUND); } // Check if cRLIssuer element present result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 2); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); // The CRL Issuer is encoded as a directoryName field of the GeneralName: // GeneralName ::= CHOICE { // otherName [0] OtherName, // rfc822Name [1] IA5String, // dNSName [2] IA5String, // x400Address [3] ORAddress, // directoryName [4] Name, // ediPartyName [5] EDIPartyName, // uniformResourceIdentifier [6] IA5String, // iPAddress [7] OCTET STRING, // registeredID [8] OBJECT IDENTIFIER } result = mbedtls_asn1_get_tag( &p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_X509_SAN_DIRECTORY_NAME); VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); error = CopySpanToMutableSpan(ByteSpan(p, len), crlIssuer); SuccessOrExit(error); } p = end_of_ext; } VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) crlIssuer; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; uint8_t * p = nullptr; size_t len = 0; mbedtls_x509_crt mbed_cert; mbedtls_x509_crt_init(&mbed_cert); result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(p); len = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(len); VerifyOrExit(len <= serialNumber.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(serialNumber.data(), p, len); serialNumber.reduce_size(len); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) serialNumber; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; mbedtls_x509_crt mbed_cert; mbedtls_asn1_named_data * dnIterator = nullptr; AttestationCertVidPid vidpidFromCN; mbedtls_x509_crt_init(&mbed_cert); int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); for (dnIterator = &mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(subject); dnIterator != nullptr; dnIterator = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(next)) { DNAttrType attrType = DNAttrType::kUnspecified; if (OID_CMP(sOID_AttributeType_CommonName, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) { attrType = DNAttrType::kCommonName; } else if (OID_CMP(sOID_AttributeType_MatterVendorId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) { attrType = DNAttrType::kMatterVID; } else if (OID_CMP(sOID_AttributeType_MatterProductId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) { attrType = DNAttrType::kMatterPID; } size_t val_len = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(len); uint8_t * val_p = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(p); error = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(val_p, val_len), vidpid, vidpidFromCN); SuccessOrExit(error); } // If Matter Attributes were not found use values extracted from the CN Attribute, // which might be uninitialized as well. if (!vidpid.Initialized()) { vidpid = vidpidFromCN; } exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbed_cert); #else (void) certificate; (void) vidpid; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } namespace { CHIP_ERROR ExtractRawDNFromX509Cert(bool extractSubject, const ByteSpan & certificate, MutableByteSpan & dn) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; uint8_t * p = nullptr; size_t len = 0; mbedtls_x509_crt mbedCertificate; VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); mbedtls_x509_crt_init(&mbedCertificate); result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); if (extractSubject) { len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); } else { len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); } VerifyOrExit(len <= dn.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(dn.data(), p, len); dn.reduce_size(len); exit: _log_mbedTLS_error(result); mbedtls_x509_crt_free(&mbedCertificate); #else (void) certificate; (void) dn; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } } // namespace CHIP_ERROR ExtractSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) { return ExtractRawDNFromX509Cert(true, certificate, subject); } CHIP_ERROR ExtractIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & issuer) { return ExtractRawDNFromX509Cert(false, certificate, issuer); } CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, size_t candidateCertificatesCount, ByteSpan & outCertificate) { #if defined(MBEDTLS_X509_CRT_PARSE_C) uint8_t referenceSubjectBuf[kMaxCertificateDistinguishedNameLength]; uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; MutableByteSpan referenceSubject(referenceSubjectBuf); MutableByteSpan referenceSKID(referenceSKIDBuf); outCertificate = referenceCertificate; VerifyOrReturnError(candidateCertificates != nullptr && candidateCertificatesCount != 0, CHIP_NO_ERROR); ReturnErrorOnFailure(ExtractSubjectFromX509Cert(referenceCertificate, referenceSubject)); ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); for (size_t i = 0; i < candidateCertificatesCount; i++) { const ByteSpan candidateCertificate = candidateCertificates[i]; uint8_t candidateSubjectBuf[kMaxCertificateDistinguishedNameLength]; uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; MutableByteSpan candidateSubject(candidateSubjectBuf); MutableByteSpan candidateSKID(candidateSKIDBuf); ReturnErrorOnFailure(ExtractSubjectFromX509Cert(candidateCertificate, candidateSubject)); ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) { outCertificate = candidateCertificate; return CHIP_NO_ERROR; } } return CHIP_NO_ERROR; #else (void) referenceCertificate; (void) candidateCertificates; (void) candidateCertificatesCount; (void) outCertificate; return CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) } } // namespace Crypto } // namespace chip