/* * * 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 * openSSL based implementation of CHIP crypto primitives */ #include "CHIPCryptoPAL.h" #include #if CHIP_CRYPTO_BORINGSSL #include #endif // CHIP_CRYPTO_BORINGSSL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace Crypto { // BoringSSL is designed to implement the same interface as OpenSSL in most // cases. However, it removes some APIs that can allow very weak configuration. // (Example: CCM ciphers with low tag lengths.) In order to support Matter, // a more specific inteface is required. #if CHIP_CRYPTO_BORINGSSL #define RAND_priv_bytes RAND_bytes #define BN_CTX_secure_new BN_CTX_new #define EC_GROUP_clear_free EC_GROUP_free using boringssl_size_t_openssl_int = size_t; using boringssl_uint_openssl_int = unsigned int; using libssl_err_type = uint32_t; #else using boringssl_size_t_openssl_int = int; using boringssl_uint_openssl_int = int; using libssl_err_type = unsigned long; #endif // CHIP_CRYPTO_BORINGSSL #define kKeyLengthInBits 256 typedef struct stack_st_X509 X509_LIST; enum class DigestType { SHA256 }; enum class ECName { None = 0, P256v1 = 1, }; static int _nidForCurve(ECName name) { switch (name) { case ECName::P256v1: return EC_curve_nist2nid("P-256"); break; default: return NID_undef; break; } } static void _logSSLError() { unsigned long ssl_err_code = ERR_get_error(); while (ssl_err_code != 0) { #if CHIP_ERROR_LOGGING const char * err_str_lib = ERR_lib_error_string(static_cast(ssl_err_code)); const char * err_str_routine = ERR_func_error_string(static_cast(ssl_err_code)); const char * err_str_reason = ERR_reason_error_string(static_cast(ssl_err_code)); if (err_str_lib) { ChipLogError(Crypto, " ssl err %s %s %s\n", StringOrNullMarker(err_str_lib), StringOrNullMarker(err_str_routine), StringOrNullMarker(err_str_reason)); } #endif // CHIP_ERROR_LOGGING ssl_err_code = ERR_get_error(); } } static const EVP_MD * _digestForType(DigestType digestType) { switch (digestType) { case DigestType::SHA256: return EVP_sha256(); break; default: return nullptr; break; } } static int _compareDaysAndSeconds(const int days, const int seconds) { if (days > 0 || seconds > 0) return 1; if (days < 0 || seconds < 0) return -1; 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) { #if CHIP_CRYPTO_BORINGSSL EVP_AEAD_CTX * context = nullptr; size_t written_tag_len = 0; const EVP_AEAD * aead = nullptr; #else EVP_CIPHER_CTX * context = nullptr; int bytesWritten = 0; size_t ciphertext_length = 0; const EVP_CIPHER * type = nullptr; #endif CHIP_ERROR error = CHIP_NO_ERROR; int result = 1; // Placeholder location for avoiding null params for plaintexts when // size is zero. uint8_t placeholder_empty_plaintext = 0; // Ciphertext block to hold a finalized ciphertext block if output // `ciphertext` buffer is nullptr or plaintext_length is zero (i.e. // we are only doing auth and don't care about output). uint8_t placeholder_ciphertext[kAES_CCM128_Block_Length]; bool ciphertext_was_null = (ciphertext == nullptr); if (plaintext_length == 0) { if (plaintext == nullptr) { plaintext = &placeholder_empty_plaintext; } // Make sure we have at least 1 full block size buffer for the // extraction of final block (required by OpenSSL EVP_EncryptFinal_ex) if (ciphertext_was_null) { ciphertext = &placeholder_ciphertext[0]; } } VerifyOrExit((plaintext_length != 0) || ciphertext_was_null, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(plaintext != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr, 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 CHIP_CRYPTO_BORINGSSL VerifyOrExit(tag_length == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, error = CHIP_ERROR_INVALID_ARGUMENT); #else VerifyOrExit(tag_length == 8 || tag_length == 12 || tag_length == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, error = CHIP_ERROR_INVALID_ARGUMENT); #endif // CHIP_CRYPTO_BORINGSSL #if CHIP_CRYPTO_BORINGSSL aead = EVP_aead_aes_128_ccm_matter(); context = EVP_AEAD_CTX_new(aead, key.As(), sizeof(Symmetric128BitsKeyByteArray), tag_length); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EVP_AEAD_CTX_seal_scatter(context, ciphertext, tag, &written_tag_len, tag_length, nonce, nonce_length, plaintext, plaintext_length, nullptr, 0, aad, aad_length); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit(written_tag_len == tag_length, error = CHIP_ERROR_INTERNAL); #else type = EVP_aes_128_ccm(); context = EVP_CIPHER_CTX_new(); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); // Pass in cipher result = EVP_EncryptInit_ex(context, type, nullptr, nullptr, nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in nonce length. Cast is safe because we checked with CanCastTo. result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_IVLEN, static_cast(nonce_length), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in tag length. Cast is safe because we checked against CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES. result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_TAG, static_cast(tag_length), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in key + nonce static_assert(kAES_CCM128_Key_Length == sizeof(Symmetric128BitsKeyByteArray), "Unexpected key length"); result = EVP_EncryptInit_ex(context, nullptr, nullptr, key.As(), Uint8::to_const_uchar(nonce)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in plain text length VerifyOrExit(CanCastTo(plaintext_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_EncryptUpdate(context, nullptr, &bytesWritten, nullptr, static_cast(plaintext_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in AAD if (aad_length > 0 && aad != nullptr) { VerifyOrExit(CanCastTo(aad_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_EncryptUpdate(context, nullptr, &bytesWritten, Uint8::to_const_uchar(aad), static_cast(aad_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); } // Encrypt VerifyOrExit(CanCastTo(plaintext_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_EncryptUpdate(context, Uint8::to_uchar(ciphertext), &bytesWritten, Uint8::to_const_uchar(plaintext), static_cast(plaintext_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit((ciphertext_was_null && bytesWritten == 0) || (bytesWritten >= 0), error = CHIP_ERROR_INTERNAL); ciphertext_length = static_cast(bytesWritten); // Finalize encryption result = EVP_EncryptFinal_ex(context, ciphertext + ciphertext_length, &bytesWritten); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit(bytesWritten >= 0 && bytesWritten <= static_cast(plaintext_length), error = CHIP_ERROR_INTERNAL); // Get tag VerifyOrExit(CanCastTo(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_GET_TAG, static_cast(tag_length), Uint8::to_uchar(tag)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); #endif // CHIP_CRYPTO_BORINGSSL exit: if (context != nullptr) { #if CHIP_CRYPTO_BORINGSSL EVP_AEAD_CTX_free(context); #else EVP_CIPHER_CTX_free(context); #endif // CHIP_CRYPTO_BORINGSSL context = nullptr; } return error; } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { #if CHIP_CRYPTO_BORINGSSL EVP_AEAD_CTX * context = nullptr; const EVP_AEAD * aead = nullptr; #else EVP_CIPHER_CTX * context = nullptr; int bytesOutput = 0; const EVP_CIPHER * type = nullptr; #endif // CHIP_CRYPTO_BORINGSSL CHIP_ERROR error = CHIP_NO_ERROR; int result = 1; // Placeholder location for avoiding null params for ciphertext when // size is zero. uint8_t placeholder_empty_ciphertext = 0; // Plaintext block to hold a finalized plaintext block if output // `plaintext` buffer is nullptr or ciphertext_length is zero (i.e. // we are only doing auth and don't care about output). uint8_t placeholder_plaintext[kAES_CCM128_Block_Length]; bool plaintext_was_null = (plaintext == nullptr); if (ciphertext_length == 0) { if (ciphertext == nullptr) { ciphertext = &placeholder_empty_ciphertext; } // Make sure we have at least 1 full block size buffer for the // extraction of final block (required by OpenSSL EVP_DecryptFinal_ex) if (plaintext_was_null) { plaintext = &placeholder_plaintext[0]; } } VerifyOrExit(ciphertext != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(plaintext != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); #if CHIP_CRYPTO_BORINGSSL VerifyOrExit(tag_length == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, error = CHIP_ERROR_INVALID_ARGUMENT); #else VerifyOrExit(tag_length == 8 || tag_length == 12 || tag_length == CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, error = CHIP_ERROR_INVALID_ARGUMENT); #endif // CHIP_CRYPTO_BORINGSSL VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); #if CHIP_CRYPTO_BORINGSSL aead = EVP_aead_aes_128_ccm_matter(); context = EVP_AEAD_CTX_new(aead, key.As(), sizeof(Symmetric128BitsKeyByteArray), tag_length); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EVP_AEAD_CTX_open_gather(context, plaintext, nonce, nonce_length, ciphertext, ciphertext_length, tag, tag_length, aad, aad_length); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); #else type = EVP_aes_128_ccm(); context = EVP_CIPHER_CTX_new(); VerifyOrExit(context != nullptr, error = CHIP_ERROR_NO_MEMORY); // Pass in cipher result = EVP_DecryptInit_ex(context, type, nullptr, nullptr, nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in nonce length VerifyOrExit(CanCastTo(nonce_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_IVLEN, static_cast(nonce_length), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in expected tag // Removing "const" from |tag| here should hopefully be safe as // we're writing the tag, not reading. VerifyOrExit(CanCastTo(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_TAG, static_cast(tag_length), const_cast(static_cast(tag))); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in key + nonce static_assert(kAES_CCM128_Key_Length == sizeof(Symmetric128BitsKeyByteArray), "Unexpected key length"); result = EVP_DecryptInit_ex(context, nullptr, nullptr, key.As(), Uint8::to_const_uchar(nonce)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Pass in cipher text length VerifyOrExit(CanCastTo(ciphertext_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_DecryptUpdate(context, nullptr, &bytesOutput, nullptr, static_cast(ciphertext_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit(bytesOutput <= static_cast(ciphertext_length), error = CHIP_ERROR_INTERNAL); // Pass in aad if (aad_length > 0 && aad != nullptr) { VerifyOrExit(CanCastTo(aad_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_DecryptUpdate(context, nullptr, &bytesOutput, Uint8::to_const_uchar(aad), static_cast(aad_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit(bytesOutput <= static_cast(aad_length), error = CHIP_ERROR_INTERNAL); } // Pass in ciphertext. We wont get anything if validation fails. VerifyOrExit(CanCastTo(ciphertext_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_DecryptUpdate(context, Uint8::to_uchar(plaintext), &bytesOutput, Uint8::to_const_uchar(ciphertext), static_cast(ciphertext_length)); if (plaintext_was_null) { VerifyOrExit(bytesOutput <= static_cast(sizeof(placeholder_plaintext)), error = CHIP_ERROR_INTERNAL); } VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); #endif // CHIP_CRYPTO_BORINGSSL exit: if (context != nullptr) { #if CHIP_CRYPTO_BORINGSSL EVP_AEAD_CTX_free(context); #else EVP_CIPHER_CTX_free(context); #endif // CHIP_CRYPTO_BORINGSSL context = nullptr; } return error; } CHIP_ERROR Hash_SHA256(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) { // zero data length hash is supported. VerifyOrReturnError(data != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); SHA256(data, data_length, Uint8::to_uchar(out_buffer)); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA1(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) { // zero data length hash is supported. VerifyOrReturnError(data != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); SHA1(data, data_length, Uint8::to_uchar(out_buffer)); return CHIP_NO_ERROR; } Hash_SHA256_stream::Hash_SHA256_stream() {} Hash_SHA256_stream::~Hash_SHA256_stream() { Clear(); } static_assert(kMAX_Hash_SHA256_Context_Size >= sizeof(SHA256_CTX), "kMAX_Hash_SHA256_Context_Size is too small for the size of underlying SHA256_CTX"); static inline SHA256_CTX * to_inner_hash_sha256_context(HashSHA256OpaqueContext * context) { return SafePointerCast(context); } CHIP_ERROR Hash_SHA256_stream::Begin() { SHA256_CTX * const context = to_inner_hash_sha256_context(&mContext); const int result = SHA256_Init(context); VerifyOrReturnError(result == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA256_stream::AddData(const ByteSpan data) { SHA256_CTX * const context = to_inner_hash_sha256_context(&mContext); const int result = SHA256_Update(context, Uint8::to_const_uchar(data.data()), data.size()); VerifyOrReturnError(result == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Hash_SHA256_stream::GetDigest(MutableByteSpan & out_buffer) { SHA256_CTX * context = to_inner_hash_sha256_context(&mContext); // Back-up context as we are about to finalize the hash to extract digest. SHA256_CTX previous_ctx = *context; // Pad + compute digest, then finalize context. It is restored next line to continue. CHIP_ERROR result = Finish(out_buffer); // Restore context prior to finalization. *context = previous_ctx; return result; } CHIP_ERROR Hash_SHA256_stream::Finish(MutableByteSpan & out_buffer) { VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); SHA256_CTX * const context = to_inner_hash_sha256_context(&mContext); const int result = SHA256_Final(Uint8::to_uchar(out_buffer.data()), context); VerifyOrReturnError(result == 1, CHIP_ERROR_INTERNAL); out_buffer = out_buffer.SubSpan(0, kSHA256_Hash_Length); return CHIP_NO_ERROR; } void Hash_SHA256_stream::Clear() { OPENSSL_cleanse(this, sizeof(*this)); } 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) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 1; EVP_PKEY_CTX * const context = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr); VerifyOrExit(context != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(secret != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(secret_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); // Salt is optional if (salt_length > 0) { VerifyOrExit(salt != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } VerifyOrExit(info_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(info != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(out_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(out_buffer != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_PKEY_derive_init(context); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_CTX_set_hkdf_md(context, EVP_sha256()); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(secret_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_PKEY_CTX_set1_hkdf_key(context, Uint8::to_const_uchar(secret), static_cast(secret_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); if (salt_length > 0 && salt != nullptr) { VerifyOrExit(CanCastTo(salt_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_PKEY_CTX_set1_hkdf_salt(context, Uint8::to_const_uchar(salt), static_cast(salt_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); } VerifyOrExit(CanCastTo(info_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = EVP_PKEY_CTX_add1_hkdf_info(context, Uint8::to_const_uchar(info), static_cast(info_length)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_CTX_hkdf_mode(context, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Get the OKM (Output Key Material) result = EVP_PKEY_derive(context, Uint8::to_uchar(out_buffer), &out_length); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); exit: if (context != nullptr) { EVP_PKEY_CTX_free(context); } 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_ERROR_INTERNAL; int error_openssl = 0; unsigned int mac_out_len = 0; HMAC_CTX * mac_ctx = HMAC_CTX_new(); VerifyOrExit(mac_ctx != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(key_length), error = CHIP_ERROR_INVALID_ARGUMENT); error_openssl = HMAC_Init_ex(mac_ctx, Uint8::to_const_uchar(key), static_cast(key_length), EVP_sha256(), nullptr); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); error_openssl = HMAC_Update(mac_ctx, Uint8::to_const_uchar(message), message_length); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); mac_out_len = static_cast(CHIP_CRYPTO_HASH_LEN_BYTES); error_openssl = HMAC_Final(mac_ctx, Uint8::to_uchar(out_buffer), &mac_out_len); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); error = CHIP_NO_ERROR; exit: HMAC_CTX_free(mac_ctx); 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) { CHIP_ERROR error = CHIP_NO_ERROR; int result = 1; const EVP_MD * md = nullptr; 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); md = _digestForType(DigestType::SHA256); VerifyOrExit(md != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(plen), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(slen), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(iteration_count), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(CanCastTo(key_length), error = CHIP_ERROR_INVALID_ARGUMENT); result = PKCS5_PBKDF2_HMAC(Uint8::to_const_char(password), static_cast(plen), Uint8::to_const_uchar(salt), static_cast(slen), static_cast(iteration_count), md, static_cast(key_length), Uint8::to_uchar(output)); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); exit: if (error != CHIP_NO_ERROR) { _logSSLError(); } return error; } CHIP_ERROR add_entropy_source(entropy_source fn_source, void * p_source, size_t 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); VerifyOrReturnError(CanCastTo(out_length), CHIP_ERROR_INVALID_ARGUMENT); const int result = RAND_priv_bytes(Uint8::to_uchar(out_buffer), static_cast(out_length)); VerifyOrReturnError(result == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } ECName MapECName(SupportedECPKeyTypes keyType) { switch (keyType) { case SupportedECPKeyTypes::ECP256R1: return ECName::P256v1; default: return ECName::None; } } static inline void from_EC_KEY(EC_KEY * key, P256KeypairContext * context) { *SafePointerCast(context) = key; } static inline EC_KEY * to_EC_KEY(P256KeypairContext * context) { return *SafePointerCast(context); } static inline const EC_KEY * to_const_EC_KEY(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; int nid = NID_undef; EC_KEY * ec_key = nullptr; ECDSA_SIG * sig = nullptr; const BIGNUM * r = nullptr; const BIGNUM * s = nullptr; VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT); uint8_t digest[kSHA256_Hash_Length]; memset(&digest[0], 0, sizeof(digest)); ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0])); ERR_clear_error(); static_assert(P256ECDSASignature::Capacity() >= kP256_ECDSA_Signature_Length_Raw, "P256ECDSASignature must be large enough"); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); nid = _nidForCurve(MapECName(mPublicKey.Type())); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); ec_key = to_EC_KEY(&mKeypair); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); sig = ECDSA_do_sign(Uint8::to_const_uchar(&digest[0]), static_cast(sizeof(digest)), ec_key); VerifyOrExit(sig != nullptr, error = CHIP_ERROR_INTERNAL); ECDSA_SIG_get0(sig, &r, &s); VerifyOrExit((r != nullptr) && (s != nullptr), error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(BN_num_bytes(r)) && CanCastTo(BN_num_bytes(s)), error = CHIP_ERROR_INTERNAL); VerifyOrExit((static_cast(BN_num_bytes(r)) <= kP256_FE_Length) && (static_cast(BN_num_bytes(s)) <= kP256_FE_Length), error = CHIP_ERROR_INTERNAL); // Concatenate r and s to output. Sizes were checked above. VerifyOrExit(out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); VerifyOrExit(BN_bn2binpad(r, out_signature.Bytes() + 0u, kP256_FE_Length) == kP256_FE_Length, error = CHIP_ERROR_INTERNAL); VerifyOrExit(BN_bn2binpad(s, out_signature.Bytes() + kP256_FE_Length, kP256_FE_Length) == kP256_FE_Length, error = CHIP_ERROR_INTERNAL); exit: if (sig != nullptr) { // SIG owns the memory of r, s ECDSA_SIG_free(sig); } if (error != CHIP_NO_ERROR) { _logSSLError(); } return error; } CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const P256ECDSASignature & signature) const { VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT); uint8_t digest[kSHA256_Hash_Length]; memset(&digest[0], 0, sizeof(digest)); ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0])); return ECDSA_validate_hash_signature(&digest[0], sizeof(digest), signature); } CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, const P256ECDSASignature & signature) const { ERR_clear_error(); CHIP_ERROR error = CHIP_ERROR_INTERNAL; int nid = NID_undef; EC_KEY * ec_key = nullptr; EC_POINT * key_point = nullptr; EC_GROUP * ec_group = nullptr; ECDSA_SIG * ec_sig = nullptr; BIGNUM * r = nullptr; BIGNUM * s = nullptr; int result = 0; VerifyOrExit(hash != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(hash_length == kSHA256_Hash_Length, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(signature.Length() == kP256_ECDSA_Signature_Length_Raw, error = CHIP_ERROR_INVALID_ARGUMENT); nid = _nidForCurve(MapECName(Type())); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); ec_group = EC_GROUP_new_by_curve_name(nid); VerifyOrExit(ec_group != nullptr, error = CHIP_ERROR_NO_MEMORY); key_point = EC_POINT_new(ec_group); VerifyOrExit(key_point != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EC_POINT_oct2point(ec_group, key_point, Uint8::to_const_uchar(*this), Length(), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); ec_key = EC_KEY_new_by_curve_name(nid); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_NO_MEMORY); result = EC_KEY_set_public_key(ec_key, key_point); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EC_KEY_check_key(ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // Build-up the signature object from raw tuple r = BN_bin2bn(Uint8::to_const_uchar(signature.ConstBytes()) + 0u, kP256_FE_Length, nullptr); VerifyOrExit(r != nullptr, error = CHIP_ERROR_NO_MEMORY); s = BN_bin2bn(Uint8::to_const_uchar(signature.ConstBytes()) + kP256_FE_Length, kP256_FE_Length, nullptr); VerifyOrExit(s != nullptr, error = CHIP_ERROR_NO_MEMORY); ec_sig = ECDSA_SIG_new(); VerifyOrExit(ec_sig != nullptr, error = CHIP_ERROR_NO_MEMORY); result = ECDSA_SIG_set0(ec_sig, r, s); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = ECDSA_do_verify(Uint8::to_const_uchar(hash), static_cast(hash_length), ec_sig, ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INVALID_SIGNATURE); error = CHIP_NO_ERROR; exit: _logSSLError(); if (ec_sig != nullptr) { ECDSA_SIG_free(ec_sig); // After ECDSA_SIG_set0 succeeds, r and s memory is managed by ECDSA_SIG object. // We set to nullptr so that we don't try to double-free r = nullptr; s = nullptr; } if (s != nullptr) { BN_clear_free(s); } if (r != nullptr) { BN_clear_free(r); } if (ec_key != nullptr) { EC_KEY_free(ec_key); } if (key_point != nullptr) { EC_POINT_clear_free(key_point); } if (ec_group != nullptr) { EC_GROUP_free(ec_group); } return error; } // helper function to populate octet key into EVP_PKEY out_evp_pkey. Caller must free out_evp_pkey static CHIP_ERROR _create_evp_key_from_binary_p256_key(const P256PublicKey & key, EVP_PKEY ** out_evp_pkey) { CHIP_ERROR error = CHIP_NO_ERROR; EC_KEY * ec_key = nullptr; int result = -1; EC_POINT * point = nullptr; EC_GROUP * group = nullptr; int nid = NID_undef; VerifyOrExit(*out_evp_pkey == nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); nid = _nidForCurve(MapECName(key.Type())); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INTERNAL); ec_key = EC_KEY_new_by_curve_name(nid); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); group = EC_GROUP_new_by_curve_name(nid); VerifyOrExit(group != nullptr, error = CHIP_ERROR_INTERNAL); point = EC_POINT_new(group); VerifyOrExit(point != nullptr, error = CHIP_ERROR_INTERNAL); result = EC_POINT_oct2point(group, point, Uint8::to_const_uchar(key), key.Length(), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EC_KEY_set_public_key(ec_key, point); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); *out_evp_pkey = EVP_PKEY_new(); VerifyOrExit(*out_evp_pkey != nullptr, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_set1_EC_KEY(*out_evp_pkey, ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); exit: if (ec_key != nullptr) { EC_KEY_free(ec_key); ec_key = nullptr; } if (error != CHIP_NO_ERROR && *out_evp_pkey) { EVP_PKEY_free(*out_evp_pkey); out_evp_pkey = nullptr; } if (point != nullptr) { EC_POINT_free(point); point = nullptr; } if (group != nullptr) { EC_GROUP_free(group); group = nullptr; } return error; } CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { ERR_clear_error(); CHIP_ERROR error = CHIP_NO_ERROR; int result = -1; EVP_PKEY * local_key = nullptr; EVP_PKEY * remote_key = nullptr; EVP_PKEY_CTX * context = nullptr; size_t out_buf_length = 0; EC_KEY * ec_key = EC_KEY_dup(to_const_EC_KEY(&mKeypair)); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); local_key = EVP_PKEY_new(); VerifyOrExit(local_key != nullptr, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_set1_EC_KEY(local_key, ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); error = _create_evp_key_from_binary_p256_key(remote_public_key, &remote_key); SuccessOrExit(error); context = EVP_PKEY_CTX_new(local_key, nullptr); VerifyOrExit(context != nullptr, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_derive_init(context); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_derive_set_peer(context, remote_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); out_buf_length = (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(); result = EVP_PKEY_derive(context, out_secret.Bytes(), &out_buf_length); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); SuccessOrExit(error = out_secret.SetLength(out_buf_length)); exit: if (ec_key != nullptr) { EC_KEY_free(ec_key); ec_key = nullptr; } if (local_key != nullptr) { EVP_PKEY_free(local_key); local_key = nullptr; } if (remote_key != nullptr) { EVP_PKEY_free(remote_key); remote_key = nullptr; } if (context != nullptr) { EVP_PKEY_CTX_free(context); context = nullptr; } _logSSLError(); return error; } void ClearSecretData(uint8_t * buf, size_t len) { OPENSSL_cleanse(buf, len); } bool IsBufferContentEqualConstantTime(const void * a, const void * b, size_t n) { return CRYPTO_memcmp(a, b, n) == 0; } static CHIP_ERROR P256PublicKeyFromECKey(EC_KEY * ec_key, P256PublicKey & pubkey) { ERR_clear_error(); CHIP_ERROR error = CHIP_NO_ERROR; int nid = NID_undef; ECName curve = MapECName(pubkey.Type()); EC_GROUP * group = nullptr; size_t pubkey_size = 0; const EC_POINT * pubkey_ecp = EC_KEY_get0_public_key(ec_key); VerifyOrExit(pubkey_ecp != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); nid = _nidForCurve(curve); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); group = EC_GROUP_new_by_curve_name(nid); VerifyOrExit(group != nullptr, error = CHIP_ERROR_INTERNAL); pubkey_size = EC_POINT_point2oct(group, pubkey_ecp, POINT_CONVERSION_UNCOMPRESSED, Uint8::to_uchar(pubkey), pubkey.Length(), nullptr); pubkey_ecp = nullptr; VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INVALID_ARGUMENT); exit: if (group != nullptr) { EC_GROUP_free(group); group = nullptr; } _logSSLError(); return error; } CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) { ERR_clear_error(); Clear(); CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; EC_KEY * ec_key = nullptr; ECName curve = MapECName(mPublicKey.Type()); int nid = _nidForCurve(curve); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); ec_key = EC_KEY_new_by_curve_name(nid); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); result = EC_KEY_generate_key(ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); error = P256PublicKeyFromECKey(ec_key, mPublicKey); SuccessOrExit(error); from_EC_KEY(ec_key, &mKeypair); mInitialized = true; ec_key = nullptr; exit: if (ec_key != nullptr) { EC_KEY_free(ec_key); ec_key = nullptr; } _logSSLError(); return error; } CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const { CHIP_ERROR error = CHIP_NO_ERROR; const EC_KEY * ec_key = to_const_EC_KEY(&mKeypair); uint8_t privkey[kP256_PrivateKey_Length]; int privkey_size = 0; const BIGNUM * privkey_bn = EC_KEY_get0_private_key(ec_key); VerifyOrExit(privkey_bn != nullptr, error = CHIP_ERROR_INTERNAL); privkey_size = BN_bn2binpad(privkey_bn, privkey, sizeof(privkey)); privkey_bn = nullptr; VerifyOrExit(privkey_size > 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit((size_t) privkey_size == sizeof(privkey), error = CHIP_ERROR_INTERNAL); { size_t len = output.Length() == 0 ? output.Capacity() : output.Length(); Encoding::BufferWriter bbuf(output.Bytes(), len); bbuf.Put(mPublicKey, mPublicKey.Length()); bbuf.Put(privkey, sizeof(privkey)); VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); output.SetLength(bbuf.Needed()); } exit: ClearSecretData(privkey, sizeof(privkey)); _logSSLError(); return error; } CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) { Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length()); Clear(); BIGNUM * pvt_key = nullptr; EC_GROUP * group = nullptr; EC_POINT * key_point = nullptr; EC_KEY * ec_key = nullptr; ECName curve = MapECName(mPublicKey.Type()); ERR_clear_error(); CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; int nid = NID_undef; const uint8_t * privkey = input.ConstBytes() + mPublicKey.Length(); VerifyOrExit(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, error = CHIP_ERROR_INVALID_ARGUMENT); bbuf.Put(input.ConstBytes(), mPublicKey.Length()); VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); nid = _nidForCurve(curve); VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); group = EC_GROUP_new_by_curve_name(nid); VerifyOrExit(group != nullptr, error = CHIP_ERROR_INTERNAL); key_point = EC_POINT_new(group); VerifyOrExit(key_point != nullptr, error = CHIP_ERROR_INTERNAL); result = EC_POINT_oct2point(group, key_point, Uint8::to_const_uchar(mPublicKey), mPublicKey.Length(), nullptr); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); ec_key = EC_KEY_new_by_curve_name(nid); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); result = EC_KEY_set_public_key(ec_key, key_point); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); pvt_key = BN_bin2bn(privkey, kP256_PrivateKey_Length, nullptr); VerifyOrExit(pvt_key != nullptr, error = CHIP_ERROR_INTERNAL); result = EC_KEY_set_private_key(ec_key, pvt_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); from_EC_KEY(ec_key, &mKeypair); mInitialized = true; ec_key = nullptr; exit: if (ec_key != nullptr) { EC_KEY_free(ec_key); ec_key = nullptr; } if (group != nullptr) { EC_GROUP_free(group); group = nullptr; } if (pvt_key != nullptr) { BN_free(pvt_key); pvt_key = nullptr; } if (key_point != nullptr) { EC_POINT_free(key_point); key_point = nullptr; } _logSSLError(); return error; } void P256Keypair::Clear() { if (mInitialized) { EC_KEY * ec_key = to_EC_KEY(&mKeypair); EC_KEY_free(ec_key); mInitialized = false; } } P256Keypair::~P256Keypair() { Clear(); } CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & csr_length) const { ERR_clear_error(); CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; int csr_length_local = 0; X509_REQ * x509_req = X509_REQ_new(); EVP_PKEY * evp_pkey = nullptr; EC_KEY * ec_key = to_EC_KEY(&mKeypair); X509_NAME * subject = X509_NAME_new(); VerifyOrExit(subject != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); result = X509_REQ_set_version(x509_req, 0); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = EC_KEY_check_key(ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); evp_pkey = EVP_PKEY_new(); VerifyOrExit(evp_pkey != nullptr, error = CHIP_ERROR_INTERNAL); result = EVP_PKEY_set1_EC_KEY(evp_pkey, ec_key); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = X509_REQ_set_pubkey(x509_req, evp_pkey); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); // TODO: mbedTLS CSR parser fails if the subject name is not set (or if empty). // CHIP Spec doesn't specify the subject name that can be used. // Figure out the correct value and update this code. result = X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_ASC, Uint8::from_const_char("CSR"), -1, -1, 0); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = X509_REQ_set_subject_name(x509_req, subject); VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); result = X509_REQ_sign(x509_req, evp_pkey, EVP_sha256()); VerifyOrExit(result > 0, error = CHIP_ERROR_INTERNAL); csr_length_local = i2d_X509_REQ(x509_req, nullptr); VerifyOrExit(csr_length_local >= 0, error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(csr_length_local), error = CHIP_ERROR_BUFFER_TOO_SMALL); VerifyOrExit(static_cast(csr_length_local) <= csr_length, error = CHIP_ERROR_BUFFER_TOO_SMALL); csr_length = static_cast(i2d_X509_REQ(x509_req, &out_csr)); exit: ec_key = nullptr; if (evp_pkey != nullptr) { EVP_PKEY_free(evp_pkey); evp_pkey = nullptr; } X509_NAME_free(subject); subject = nullptr; X509_REQ_free(x509_req); _logSSLError(); return error; } CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr, size_t csr_length, P256PublicKey & pubkey) { ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr, csr_length)); ERR_clear_error(); CHIP_ERROR error = CHIP_NO_ERROR; int result = 0; EVP_PKEY * evp_pkey = nullptr; EC_KEY * ec_key = nullptr; const unsigned char * csr_buf = Uint8::to_const_uchar(csr); X509_REQ * x509_req = d2i_X509_REQ(nullptr, &csr_buf, (int) csr_length); VerifyOrExit(x509_req != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(X509_REQ_get_version(x509_req) == 0, error = CHIP_ERROR_INVALID_ARGUMENT); evp_pkey = X509_REQ_get_pubkey(x509_req); VerifyOrExit(evp_pkey != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); result = X509_REQ_verify(x509_req, evp_pkey); VerifyOrExit(result == 1, error = CHIP_ERROR_INVALID_ARGUMENT); ec_key = EVP_PKEY_get1_EC_KEY(evp_pkey); VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); error = P256PublicKeyFromECKey(ec_key, pubkey); SuccessOrExit(error); exit: if (x509_req != nullptr) { X509_REQ_free(x509_req); } if (ec_key != nullptr) { EC_KEY_free(ec_key); } if (evp_pkey != nullptr) { EVP_PKEY_free(evp_pkey); } _logSSLError(); return error; } #define init_point(_point_) \ do \ { \ _point_ = EC_POINT_new(context->curve); \ VerifyOrReturnError(_point_ != nullptr, CHIP_ERROR_INTERNAL); \ } while (0) #define init_bn(_bn_) \ do \ { \ _bn_ = BN_new(); \ VerifyOrReturnError(_bn_ != nullptr, CHIP_ERROR_INTERNAL); \ } while (0) #define free_point(_point_) \ do \ { \ if (_point_ != nullptr) \ { \ EC_POINT_clear_free(static_cast(_point_)); \ } \ } while (0) #define free_bn(_bn_) \ do \ { \ if (_bn_ != nullptr) \ { \ BN_clear_free(static_cast(_bn_)); \ } \ } while (0) typedef struct Spake2p_Context { EC_GROUP * curve; BN_CTX * bn_ctx; const EVP_MD * md_info; } Spake2p_Context; static inline Spake2p_Context * to_inner_spake2p_context(Spake2pOpaqueContext * context) { return SafePointerCast(context); } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitInternal() { Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); context->curve = nullptr; context->bn_ctx = nullptr; context->md_info = nullptr; context->curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); VerifyOrReturnError(context->curve != nullptr, CHIP_ERROR_INTERNAL); G = EC_GROUP_get0_generator(context->curve); VerifyOrReturnError(G != nullptr, CHIP_ERROR_INTERNAL); context->bn_ctx = BN_CTX_secure_new(); VerifyOrReturnError(context->bn_ctx != nullptr, CHIP_ERROR_INTERNAL); context->md_info = EVP_sha256(); VerifyOrReturnError(context->md_info != nullptr, CHIP_ERROR_INTERNAL); init_point(M); init_point(N); init_point(X); init_point(Y); init_point(L); init_point(V); init_point(Z); init_bn(w0); init_bn(w1); init_bn(xy); init_bn(tempbn); init_bn(order); const int error_openssl = EC_GROUP_get_order(context->curve, static_cast(order), context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } void Spake2p_P256_SHA256_HKDF_HMAC::Clear() { VerifyOrReturn(state != CHIP_SPAKE2P_STATE::PREINIT); Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); if (context->curve != nullptr) { EC_GROUP_clear_free(context->curve); } if (context->bn_ctx != nullptr) { BN_CTX_free(context->bn_ctx); } free_point(M); free_point(N); free_point(X); free_point(Y); free_point(L); free_point(V); free_point(Z); free_bn(w0); free_bn(w1); free_bn(xy); free_bn(tempbn); free_bn(order); 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) { VerifyOrReturnError(mac_len == kSHA256_Hash_Length, CHIP_ERROR_INVALID_ARGUMENT); uint8_t computed_mac[kSHA256_Hash_Length]; MutableByteSpan computed_mac_span{ computed_mac }; ReturnErrorOnFailure(Mac(key, key_len, in, in_len, computed_mac_span)); VerifyOrReturnError(computed_mac_span.size() == mac_len, CHIP_ERROR_INTERNAL); VerifyOrReturnError(CRYPTO_memcmp(mac, computed_mac_span.data(), computed_mac_span.size()) == 0, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FELoad(const uint8_t * in, size_t in_len, void * fe) { BIGNUM * const bn_fe = static_cast(fe); Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); VerifyOrReturnError(CanCastTo(in_len), CHIP_ERROR_INTERNAL); BN_bin2bn(Uint8::to_const_uchar(in), static_cast(in_len), bn_fe); const int error_openssl = BN_mod(bn_fe, bn_fe, (BIGNUM *) order, context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEWrite(const void * fe, uint8_t * out, size_t out_len) { VerifyOrReturnError(CanCastTo(out_len), CHIP_ERROR_INTERNAL); const int bn_out_len = BN_bn2binpad(static_cast(fe), Uint8::to_uchar(out), static_cast(out_len)); VerifyOrReturnError(bn_out_len == static_cast(out_len), CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEGenerate(void * fe) { const int error_openssl = BN_rand_range(static_cast(fe), static_cast(order)); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEMul(void * fer, const void * fe1, const void * fe2) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const int error_openssl = BN_mod_mul(static_cast(fer), static_cast(fe1), static_cast(fe2), static_cast(order), context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointLoad(const uint8_t * in, size_t in_len, void * R) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const int error_openssl = EC_POINT_oct2point(context->curve, static_cast(R), Uint8::to_const_uchar(in), in_len, context->bn_ctx); VerifyOrReturnError(error_openssl == 1, 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) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const size_t ec_out_len = EC_POINT_point2oct(context->curve, static_cast(R), POINT_CONVERSION_UNCOMPRESSED, Uint8::to_uchar(out), out_len, context->bn_ctx); VerifyOrReturnError(ec_out_len == out_len, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointMul(void * R, const void * P1, const void * fe1) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const int error_openssl = EC_POINT_mul(context->curve, static_cast(R), nullptr, static_cast(P1), static_cast(fe1), context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); 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) { CHIP_ERROR error = CHIP_ERROR_INTERNAL; int error_openssl = 0; EC_POINT * scratch = nullptr; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); scratch = EC_POINT_new(context->curve); VerifyOrExit(scratch != nullptr, error = CHIP_ERROR_INTERNAL); SuccessOrExit(error = PointMul(scratch, P1, fe1)); SuccessOrExit(error = PointMul(R, P2, fe2)); error_openssl = EC_POINT_add(context->curve, static_cast(R), static_cast(R), static_cast(scratch), context->bn_ctx); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); error = CHIP_NO_ERROR; exit: EC_POINT_clear_free(scratch); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointInvert(void * R) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const int error_openssl = EC_POINT_invert(context->curve, static_cast(R), context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointCofactorMul(void * R) { // Cofactor on P256 is 1 so this is a NOP 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_ERROR_INTERNAL; int error_openssl = 0; BIGNUM * w1_bn = nullptr; EC_POINT * Lout_point = nullptr; Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); w1_bn = BN_new(); VerifyOrExit(w1_bn != nullptr, error = CHIP_ERROR_INTERNAL); Lout_point = EC_POINT_new(context->curve); VerifyOrExit(Lout_point != nullptr, error = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(w1in_len), error = CHIP_ERROR_INTERNAL); BN_bin2bn(Uint8::to_const_uchar(w1in), static_cast(w1in_len), w1_bn); error_openssl = BN_mod(w1_bn, w1_bn, (BIGNUM *) order, context->bn_ctx); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); error_openssl = EC_POINT_mul(context->curve, Lout_point, w1_bn, nullptr, nullptr, context->bn_ctx); VerifyOrExit(error_openssl == 1, error = CHIP_ERROR_INTERNAL); *L_len = EC_POINT_point2oct(context->curve, Lout_point, POINT_CONVERSION_UNCOMPRESSED, Uint8::to_uchar(Lout), *L_len, context->bn_ctx); VerifyOrExit(*L_len != 0, error = CHIP_ERROR_INTERNAL); error = CHIP_NO_ERROR; exit: BN_clear_free(w1_bn); EC_POINT_clear_free(Lout_point); return error; } CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointIsValid(void * R) { const Spake2p_Context * const context = to_inner_spake2p_context(&mSpake2pContext); const int error_openssl = EC_POINT_is_on_curve(context->curve, static_cast(R), context->bn_ctx); VerifyOrReturnError(error_openssl == 1, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) { CHIP_ERROR err = CHIP_NO_ERROR; const uint8_t * certPtr = cert.data(); X509 * x509Cert = nullptr; bool extBasicPresent = false; bool extKeyUsagePresent = false; bool extSKIDPresent = false; bool extAKIDPresent = false; VerifyOrReturnError(!cert.empty() && CanCastTo(cert.size()), CHIP_ERROR_INVALID_ARGUMENT); x509Cert = d2i_X509(nullptr, &certPtr, static_cast(cert.size())); VerifyOrExit(x509Cert != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_version(x509Cert) == 2, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_serialNumber(x509Cert) != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_signature_nid(x509Cert) == NID_ecdsa_with_SHA256, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_issuer_name(x509Cert) != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_notBefore(x509Cert) != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_notAfter(x509Cert) != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get_subject_name(x509Cert) != nullptr, err = CHIP_ERROR_INTERNAL); // Verify public key presence and format. { Crypto::P256PublicKey pubkey; SuccessOrExit(err = ExtractPubkeyFromX509Cert(cert, pubkey)); } for (int i = 0; i < X509_get_ext_count(x509Cert); i++) { X509_EXTENSION * ex = X509_get_ext(x509Cert, i); ASN1_OBJECT * obj = X509_EXTENSION_get_object(ex); bool isCritical = X509_EXTENSION_get_critical(ex) == 1; switch (OBJ_obj2nid(obj)) { case NID_basic_constraints: VerifyOrExit(isCritical && !extBasicPresent, err = CHIP_ERROR_INTERNAL); extBasicPresent = true; { bool isCA = X509_get_extension_flags(x509Cert) & EXFLAG_CA; long pathLen = X509_get_pathlen(x509Cert); if (certType == AttestationCertType::kDAC) { VerifyOrExit(!isCA && pathLen == -1, err = CHIP_ERROR_INTERNAL); } else if (certType == AttestationCertType::kPAI) { VerifyOrExit(isCA && pathLen == 0, err = 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), err = CHIP_ERROR_INTERNAL); } } break; case NID_key_usage: VerifyOrExit(isCritical && !extKeyUsagePresent, err = CHIP_ERROR_INTERNAL); extKeyUsagePresent = true; { uint32_t keyUsage = X509_get_key_usage(x509Cert); if (certType == AttestationCertType::kDAC) { // SHALL only have the digitalSignature bit set. VerifyOrExit(keyUsage == X509v3_KU_DIGITAL_SIGNATURE, err = CHIP_ERROR_INTERNAL); } else { bool keyCertSignFlag = keyUsage & X509v3_KU_KEY_CERT_SIGN; bool crlSignFlag = keyUsage & X509v3_KU_CRL_SIGN; bool otherFlags = keyUsage & ~static_cast(X509v3_KU_CRL_SIGN | X509v3_KU_KEY_CERT_SIGN | X509v3_KU_DIGITAL_SIGNATURE); VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, err = CHIP_ERROR_INTERNAL); } } break; case NID_subject_key_identifier: VerifyOrExit(!isCritical && !extSKIDPresent, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get0_subject_key_id(x509Cert)->length == kSubjectKeyIdentifierLength, err = CHIP_ERROR_INTERNAL); extSKIDPresent = true; break; case NID_authority_key_identifier: VerifyOrExit(!isCritical && !extAKIDPresent, err = CHIP_ERROR_INTERNAL); VerifyOrExit(X509_get0_authority_key_id(x509Cert)->length == kAuthorityKeyIdentifierLength, err = CHIP_ERROR_INTERNAL); extAKIDPresent = true; break; default: break; } } // Mandatory extensions for all certs. VerifyOrExit(extBasicPresent && extKeyUsagePresent && extSKIDPresent, err = CHIP_ERROR_INTERNAL); if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) { // Mandatory extension for DAC and PAI certs. VerifyOrExit(extAKIDPresent, err = CHIP_ERROR_INTERNAL); } exit: X509_free(x509Cert); return err; } 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) { CHIP_ERROR err = CHIP_NO_ERROR; int status = 0; X509_STORE_CTX * verifyCtx = nullptr; X509_STORE * store = nullptr; STACK_OF(X509) * chain = nullptr; X509 * x509RootCertificate = nullptr; X509 * x509CACertificate = nullptr; X509 * x509LeafCertificate = nullptr; result = CertificateChainValidationResult::kInternalFrameworkError; VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0 && CanCastTo(rootCertificateLen), (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0 && CanCastTo(leafCertificateLen), (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); store = X509_STORE_new(); VerifyOrExit(store != nullptr, (result = CertificateChainValidationResult::kNoMemory, err = CHIP_ERROR_NO_MEMORY)); verifyCtx = X509_STORE_CTX_new(); VerifyOrExit(verifyCtx != nullptr, (result = CertificateChainValidationResult::kNoMemory, err = CHIP_ERROR_NO_MEMORY)); chain = sk_X509_new_null(); VerifyOrExit(chain != nullptr, (result = CertificateChainValidationResult::kNoMemory, err = CHIP_ERROR_NO_MEMORY)); VerifyOrExit(CanCastTo(rootCertificateLen), (result = CertificateChainValidationResult::kRootArgumentInvalid, err = CHIP_ERROR_INVALID_ARGUMENT)); x509RootCertificate = d2i_X509(nullptr, &rootCertificate, static_cast(rootCertificateLen)); VerifyOrExit(x509RootCertificate != nullptr, (result = CertificateChainValidationResult::kRootFormatInvalid, err = CHIP_ERROR_INTERNAL)); status = X509_STORE_add_cert(store, x509RootCertificate); VerifyOrExit(status == 1, (result = CertificateChainValidationResult::kInternalFrameworkError, err = CHIP_ERROR_INTERNAL)); if (caCertificate != nullptr && caCertificateLen > 0) { VerifyOrExit(CanCastTo(caCertificateLen), (result = CertificateChainValidationResult::kICAArgumentInvalid, err = CHIP_ERROR_INVALID_ARGUMENT)); x509CACertificate = d2i_X509(nullptr, &caCertificate, static_cast(caCertificateLen)); VerifyOrExit(x509CACertificate != nullptr, (result = CertificateChainValidationResult::kICAFormatInvalid, err = CHIP_ERROR_INTERNAL)); status = static_cast(sk_X509_push(chain, x509CACertificate)); VerifyOrExit(status == 1, (result = CertificateChainValidationResult::kInternalFrameworkError, err = CHIP_ERROR_INTERNAL)); } VerifyOrExit(CanCastTo(leafCertificateLen), (result = CertificateChainValidationResult::kLeafArgumentInvalid, err = CHIP_ERROR_INVALID_ARGUMENT)); x509LeafCertificate = d2i_X509(nullptr, &leafCertificate, static_cast(leafCertificateLen)); VerifyOrExit(x509LeafCertificate != nullptr, (result = CertificateChainValidationResult::kLeafFormatInvalid, err = CHIP_ERROR_INTERNAL)); status = X509_STORE_CTX_init(verifyCtx, store, x509LeafCertificate, chain); VerifyOrExit(status == 1, (result = CertificateChainValidationResult::kInternalFrameworkError, err = CHIP_ERROR_INTERNAL)); // Set time used in the X509 certificate chain validation to the notBefore time of the leaf certificate. // That way the X509_verify_cert() validates that intermediate and root certificates were // valid at the time of the leaf certificate generation. { X509_VERIFY_PARAM * param = X509_STORE_CTX_get0_param(verifyCtx); chip::ASN1::ASN1UniversalTime asn1Time; char * asn1TimeStr = reinterpret_cast(X509_get_notBefore(x509LeafCertificate)->data); uint32_t unixEpoch; VerifyOrExit(param != nullptr, (result = CertificateChainValidationResult::kNoMemory, err = CHIP_ERROR_NO_MEMORY)); VerifyOrExit(CHIP_NO_ERROR == asn1Time.ImportFrom_ASN1_TIME_string(CharSpan(asn1TimeStr, strlen(asn1TimeStr))), (result = CertificateChainValidationResult::kLeafFormatInvalid, err = CHIP_ERROR_INTERNAL)); VerifyOrExit(asn1Time.ExportTo_UnixTime(unixEpoch), (result = CertificateChainValidationResult::kLeafFormatInvalid, err = CHIP_ERROR_INTERNAL)); VerifyOrExit(CanCastTo(unixEpoch), (result = CertificateChainValidationResult::kLeafFormatInvalid, err = CHIP_ERROR_INTERNAL)); X509_VERIFY_PARAM_set_time(param, static_cast(unixEpoch)); X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_X509_STRICT); } status = X509_verify_cert(verifyCtx); VerifyOrExit(status == 1, (result = CertificateChainValidationResult::kChainInvalid, err = CHIP_ERROR_CERT_NOT_TRUSTED)); err = CHIP_NO_ERROR; result = CertificateChainValidationResult::kSuccess; exit: X509_free(x509LeafCertificate); X509_free(x509CACertificate); X509_free(x509RootCertificate); sk_X509_free(chain); X509_STORE_CTX_free(verifyCtx); X509_STORE_free(store); return err; } CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate) { CHIP_ERROR error = CHIP_NO_ERROR; X509 * x509CandidateCertificate = nullptr; X509 * x509issuerCertificate = nullptr; const unsigned char * pCandidateCertificate = candidateCertificate.data(); const unsigned char * pIssuerCertificate = issuerCertificate.data(); ASN1_TIME * candidateNotBeforeTime = nullptr; ASN1_TIME * issuerNotBeforeTime = nullptr; ASN1_TIME * issuerNotAfterTime = nullptr; int result = 0; int days = 0; int seconds = 0; VerifyOrReturnError(!candidateCertificate.empty() && CanCastTo(candidateCertificate.size()) && !issuerCertificate.empty() && CanCastTo(issuerCertificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509CandidateCertificate = d2i_X509(nullptr, &pCandidateCertificate, static_cast(candidateCertificate.size())); VerifyOrExit(x509CandidateCertificate != nullptr, error = CHIP_ERROR_NO_MEMORY); x509issuerCertificate = d2i_X509(nullptr, &pIssuerCertificate, static_cast(issuerCertificate.size())); VerifyOrExit(x509issuerCertificate != nullptr, error = CHIP_ERROR_NO_MEMORY); candidateNotBeforeTime = X509_get_notBefore(x509CandidateCertificate); issuerNotBeforeTime = X509_get_notBefore(x509issuerCertificate); issuerNotAfterTime = X509_get_notAfter(x509issuerCertificate); VerifyOrExit(candidateNotBeforeTime && issuerNotBeforeTime && issuerNotAfterTime, error = CHIP_ERROR_INTERNAL); result = ASN1_TIME_diff(&days, &seconds, issuerNotBeforeTime, candidateNotBeforeTime); VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); result = _compareDaysAndSeconds(days, seconds); // check if candidateCertificate is issued at or after tbeCertificate's notBefore timestamp VerifyOrExit(result >= 0, error = CHIP_ERROR_CERT_EXPIRED); result = ASN1_TIME_diff(&days, &seconds, issuerNotAfterTime, candidateNotBeforeTime); VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); result = _compareDaysAndSeconds(days, seconds); // check if candidateCertificate is issued at or before tbeCertificate's notAfter timestamp VerifyOrExit(result <= 0, error = CHIP_ERROR_CERT_EXPIRED); exit: X509_free(x509CandidateCertificate); X509_free(x509issuerCertificate); return error; } CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) { CHIP_ERROR error = CHIP_NO_ERROR; X509 * x509Certificate = nullptr; const unsigned char * pCertificate = certificate.data(); ASN1_TIME * time = nullptr; int result = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509Certificate = d2i_X509(nullptr, &pCertificate, static_cast(certificate.size())); VerifyOrExit(x509Certificate != nullptr, error = CHIP_ERROR_NO_MEMORY); time = X509_get_notBefore(x509Certificate); VerifyOrExit(time, error = CHIP_ERROR_INTERNAL); result = X509_cmp_current_time(time); // check if certificate's notBefore timestamp is earlier than or equal to current time. VerifyOrExit(result == -1, error = CHIP_ERROR_CERT_EXPIRED); time = X509_get_notAfter(x509Certificate); VerifyOrExit(time, error = CHIP_ERROR_INTERNAL); result = X509_cmp_current_time(time); // check if certificate's notAfter timestamp is later than current time. VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); exit: X509_free(x509Certificate); return error; } CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) { CHIP_ERROR err = CHIP_NO_ERROR; EC_KEY * ec_key = nullptr; EVP_PKEY * pkey = nullptr; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); const unsigned char ** ppCertificate = &pCertificate; unsigned char * pPubkey = pubkey; unsigned char ** ppPubkey = &pPubkey; int pkeyLen; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); pkey = X509_get_pubkey(x509certificate); VerifyOrExit(pkey != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(EVP_PKEY_base_id(pkey) == EVP_PKEY_EC, err = CHIP_ERROR_INTERNAL); VerifyOrExit(EVP_PKEY_bits(pkey) == 256, err = CHIP_ERROR_INTERNAL); ec_key = EVP_PKEY_get1_EC_KEY(pkey); VerifyOrExit(ec_key != nullptr, err = CHIP_ERROR_NO_MEMORY); VerifyOrExit(EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) == NID_X9_62_prime256v1, err = CHIP_ERROR_INTERNAL); pkeyLen = i2d_PublicKey(pkey, nullptr); VerifyOrExit(pkeyLen == static_cast(pubkey.Length()), err = CHIP_ERROR_INTERNAL); VerifyOrExit(i2d_PublicKey(pkey, ppPubkey) == pkeyLen, err = CHIP_ERROR_INTERNAL); exit: EC_KEY_free(ec_key); EVP_PKEY_free(pkey); X509_free(x509certificate); return err; } namespace { CHIP_ERROR ExtractKIDFromX509Cert(bool isSKID, const ByteSpan & certificate, MutableByteSpan & kid) { CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); const unsigned char ** ppCertificate = &pCertificate; const ASN1_OCTET_STRING * kidString = nullptr; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); kidString = isSKID ? X509_get0_subject_key_id(x509certificate) : X509_get0_authority_key_id(x509certificate); VerifyOrExit(kidString != nullptr, err = CHIP_ERROR_NOT_FOUND); VerifyOrExit(CanCastTo(kidString->length), err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(kidString->length == kSubjectKeyIdentifierLength, err = CHIP_ERROR_WRONG_CERT_TYPE); VerifyOrExit(static_cast(kidString->length) <= kid.size(), err = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(kid.data(), kidString->data, static_cast(kidString->length)); kid.reduce_size(static_cast(kidString->length)); exit: X509_free(x509certificate); return err; } } // 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) { CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); const unsigned char ** ppCertificate = &pCertificate; STACK_OF(DIST_POINT) * crldp = nullptr; DIST_POINT * dp = nullptr; GENERAL_NAMES * gens = nullptr; GENERAL_NAME * gen = nullptr; ASN1_STRING * uri = nullptr; const char * urlptr = nullptr; size_t len = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); // 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) crldp = reinterpret_cast(X509_get_ext_d2i(x509certificate, NID_crl_distribution_points, nullptr, nullptr)); VerifyOrExit(crldp != nullptr, err = CHIP_ERROR_NOT_FOUND); VerifyOrExit(sk_DIST_POINT_num(crldp) == 1, err = CHIP_ERROR_NOT_FOUND); dp = sk_DIST_POINT_value(crldp, 0); VerifyOrExit(dp != nullptr, err = CHIP_ERROR_NOT_FOUND); VerifyOrExit(dp->distpoint != nullptr && dp->distpoint->type == 0, err = 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 } // // where the DistributionPointName is a CHOICE of: // DistributionPointName ::= CHOICE { // fullName [0] GeneralNames, // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } // // The URI should be encoded in the fullName element. // This implementation only supports a single GeneralName in the fullName sequence: // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName gens = dp->distpoint->name.fullname; VerifyOrExit(sk_GENERAL_NAME_num(gens) == 1, err = CHIP_ERROR_NOT_FOUND); // 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 } gen = sk_GENERAL_NAME_value(gens, 0); VerifyOrExit(gen->type == GEN_URI, err = CHIP_ERROR_NOT_FOUND); uri = reinterpret_cast(GENERAL_NAME_get0_value(gen, nullptr)); urlptr = reinterpret_cast(ASN1_STRING_get0_data(uri)); VerifyOrExit(CanCastTo(ASN1_STRING_length(uri)), err = CHIP_ERROR_NOT_FOUND); len = static_cast(ASN1_STRING_length(uri)); VerifyOrExit( (len > strlen(kValidCDPURIHttpPrefix) && strncmp(urlptr, kValidCDPURIHttpPrefix, strlen(kValidCDPURIHttpPrefix)) == 0) || (len > strlen(kValidCDPURIHttpsPrefix) && strncmp(urlptr, kValidCDPURIHttpsPrefix, strlen(kValidCDPURIHttpsPrefix)) == 0), err = CHIP_ERROR_NOT_FOUND); err = CopyCharSpanToMutableCharSpan(CharSpan(urlptr, len), cdpurl); exit: sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); X509_free(x509certificate); return err; } CHIP_ERROR ExtractCDPExtensionCRLIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & crlIssuer) { CHIP_ERROR err = CHIP_NO_ERROR; int result = 1; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); const unsigned char ** ppCertificate = &pCertificate; STACK_OF(DIST_POINT) * crldp = nullptr; DIST_POINT * dp = nullptr; GENERAL_NAMES * gens = nullptr; GENERAL_NAME * gen = nullptr; X509_NAME * dirName = nullptr; const uint8_t * pDirName = nullptr; size_t dirNameLen = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); // 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) crldp = reinterpret_cast(X509_get_ext_d2i(x509certificate, NID_crl_distribution_points, nullptr, nullptr)); VerifyOrExit(crldp != nullptr, err = CHIP_ERROR_NOT_FOUND); VerifyOrExit(sk_DIST_POINT_num(crldp) == 1, err = CHIP_ERROR_NOT_FOUND); dp = sk_DIST_POINT_value(crldp, 0); VerifyOrExit(dp != nullptr, err = 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 } // // the cRLIssuer is encoded as a GeneralNames, where: // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName // This implementation only supports a single GeneralName element in the cRLIssuer sequence: gens = dp->CRLissuer; VerifyOrExit(sk_GENERAL_NAME_num(gens) == 1, err = CHIP_ERROR_NOT_FOUND); // In this implementation the cRLIssuer is expected to be 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 } gen = sk_GENERAL_NAME_value(gens, 0); VerifyOrExit(gen->type == GEN_DIRNAME, err = CHIP_ERROR_NOT_FOUND); dirName = reinterpret_cast(GENERAL_NAME_get0_value(gen, nullptr)); VerifyOrExit(dirName != nullptr, err = CHIP_ERROR_NOT_FOUND); // Extract directoryName as a raw DER Encoded data result = X509_NAME_get0_der(dirName, &pDirName, &dirNameLen); VerifyOrExit(result == 1, err = CHIP_ERROR_INTERNAL); err = CopySpanToMutableSpan(ByteSpan(pDirName, dirNameLen), crlIssuer); exit: sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); X509_free(x509certificate); return err; } CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber) { CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509certificate = nullptr; auto * pCertificate = Uint8::to_const_uchar(certificate.data()); const unsigned char ** ppCertificate = &pCertificate; const ASN1_INTEGER * serialNumberASN1 = nullptr; size_t serialNumberLen = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); serialNumberASN1 = X509_get_serialNumber(x509certificate); VerifyOrExit(serialNumberASN1 != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(serialNumberASN1->data != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(CanCastTo(serialNumberASN1->length), err = CHIP_ERROR_INTERNAL); serialNumberLen = static_cast(serialNumberASN1->length); VerifyOrExit(serialNumberLen <= serialNumber.size(), err = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(serialNumber.data(), serialNumberASN1->data, serialNumberLen); serialNumber.reduce_size(serialNumberLen); exit: X509_free(x509certificate); return err; } namespace { CHIP_ERROR ExtractRawDNFromX509Cert(bool extractSubject, const ByteSpan & certificate, MutableByteSpan & dn) { CHIP_ERROR err = CHIP_NO_ERROR; int result = 1; X509 * x509certificate = nullptr; auto * pCertificate = Uint8::to_const_uchar(certificate.data()); const unsigned char ** ppCertificate = &pCertificate; X509_NAME * distinguishedName = nullptr; const uint8_t * pDistinguishedName = nullptr; size_t distinguishedNameLen = 0; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); if (extractSubject) { distinguishedName = X509_get_subject_name(x509certificate); } else { distinguishedName = X509_get_issuer_name(x509certificate); } VerifyOrExit(distinguishedName != nullptr, err = CHIP_ERROR_INTERNAL); result = X509_NAME_get0_der(distinguishedName, &pDistinguishedName, &distinguishedNameLen); VerifyOrExit(result == 1, err = CHIP_ERROR_INTERNAL); err = CopySpanToMutableSpan(ByteSpan(pDistinguishedName, distinguishedNameLen), dn); exit: X509_free(x509certificate); return err; } } // 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 ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) { ASN1_OBJECT * commonNameObj = OBJ_txt2obj("2.5.4.3", 1); ASN1_OBJECT * matterVidObj = OBJ_txt2obj("1.3.6.1.4.1.37244.2.1", 1); // Matter VID OID - taken from Spec ASN1_OBJECT * matterPidObj = OBJ_txt2obj("1.3.6.1.4.1.37244.2.2", 1); // Matter PID OID - taken from Spec CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); X509_NAME * subject = nullptr; int x509EntryCountIdx = 0; AttestationCertVidPid vidpidFromCN; VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); x509certificate = d2i_X509(nullptr, &pCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); subject = X509_get_subject_name(x509certificate); VerifyOrExit(subject != nullptr, err = CHIP_ERROR_INTERNAL); for (x509EntryCountIdx = 0; x509EntryCountIdx < X509_NAME_entry_count(subject); ++x509EntryCountIdx) { X509_NAME_ENTRY * name_entry = X509_NAME_get_entry(subject, x509EntryCountIdx); VerifyOrExit(name_entry != nullptr, err = CHIP_ERROR_INTERNAL); ASN1_OBJECT * object = X509_NAME_ENTRY_get_object(name_entry); VerifyOrExit(object != nullptr, err = CHIP_ERROR_INTERNAL); DNAttrType attrType = DNAttrType::kUnspecified; if (OBJ_cmp(object, commonNameObj) == 0) { attrType = DNAttrType::kCommonName; } else if (OBJ_cmp(object, matterVidObj) == 0) { attrType = DNAttrType::kMatterVID; } else if (OBJ_cmp(object, matterPidObj) == 0) { attrType = DNAttrType::kMatterPID; } if (attrType != DNAttrType::kUnspecified) { ASN1_STRING * data_entry = X509_NAME_ENTRY_get_data(name_entry); VerifyOrExit(data_entry != nullptr, err = CHIP_ERROR_INTERNAL); unsigned char * str = ASN1_STRING_data(data_entry); VerifyOrExit(str != nullptr, err = CHIP_ERROR_INTERNAL); int len = ASN1_STRING_length(data_entry); VerifyOrExit(CanCastTo(len), err = CHIP_ERROR_INTERNAL); err = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(str, static_cast(len)), vidpid, vidpidFromCN); SuccessOrExit(err); } } // 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: ASN1_OBJECT_free(commonNameObj); ASN1_OBJECT_free(matterVidObj); ASN1_OBJECT_free(matterPidObj); X509_free(x509certificate); return err; } CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, size_t candidateCertificatesCount, ByteSpan & outCertificate) { CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509ReferenceCertificate = nullptr; X509 * x509CandidateCertificate = nullptr; const uint8_t * pReferenceCertificate = referenceCertificate.data(); X509_NAME * referenceSubject = nullptr; X509_NAME * candidateSubject = nullptr; uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; MutableByteSpan referenceSKID(referenceSKIDBuf); MutableByteSpan candidateSKID(candidateSKIDBuf); VerifyOrReturnError(!referenceCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); outCertificate = referenceCertificate; VerifyOrReturnError(candidateCertificates != nullptr && candidateCertificatesCount != 0, CHIP_NO_ERROR); ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); x509ReferenceCertificate = d2i_X509(nullptr, &pReferenceCertificate, static_cast(referenceCertificate.size())); VerifyOrExit(x509ReferenceCertificate != nullptr, err = CHIP_ERROR_NO_MEMORY); referenceSubject = X509_get_subject_name(x509ReferenceCertificate); VerifyOrExit(referenceSubject != nullptr, err = CHIP_ERROR_INTERNAL); for (size_t i = 0; i < candidateCertificatesCount; i++) { const ByteSpan candidateCertificate = candidateCertificates[i]; const uint8_t * pCandidateCertificate = candidateCertificate.data(); VerifyOrExit(!candidateCertificate.empty(), err = CHIP_ERROR_INVALID_ARGUMENT); SuccessOrExit(err = ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); x509CandidateCertificate = d2i_X509(nullptr, &pCandidateCertificate, static_cast(candidateCertificate.size())); VerifyOrExit(x509CandidateCertificate != nullptr, err = CHIP_ERROR_NO_MEMORY); candidateSubject = X509_get_subject_name(x509CandidateCertificate); VerifyOrExit(candidateSubject != nullptr, err = CHIP_ERROR_INTERNAL); if (referenceSKID.data_equal(candidateSKID) && X509_NAME_cmp(referenceSubject, candidateSubject) == 0) { outCertificate = candidateCertificate; ExitNow(); } X509_free(x509CandidateCertificate); x509CandidateCertificate = nullptr; } exit: X509_free(x509ReferenceCertificate); X509_free(x509CandidateCertificate); return err; } } // namespace Crypto } // namespace chip