| /* |
| * |
| * Copyright (c) 2020 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 <openssl/conf.h> |
| #include <openssl/ecdsa.h> |
| #include <openssl/err.h> |
| #include <openssl/evp.h> |
| #include <openssl/kdf.h> |
| #include <openssl/ossl_typ.h> |
| #include <openssl/rand.h> |
| |
| #include <support/CodeUtils.h> |
| #include <support/logging/CHIPLogging.h> |
| |
| #include <string.h> |
| |
| #define kKeyLengthInBits 256 |
| |
| enum class DigestType |
| { |
| SHA256 |
| }; |
| |
| enum class ECName |
| { |
| P256v1 |
| }; |
| |
| using namespace chip::Crypto; |
| |
| static_assert(kMax_ECDH_Secret_Length >= 32, "ECDH shared secret is too short"); |
| static_assert(kMax_ECDSA_Signature_Length >= 72, "ECDSA signature buffer length is too short"); |
| |
| static int _nidForCurve(ECName name) |
| { |
| switch (name) |
| { |
| case ECName::P256v1: |
| return EC_curve_nist2nid("P-256"); |
| break; |
| |
| default: |
| return NID_undef; |
| break; |
| } |
| } |
| |
| static bool _isValidTagLength(size_t tag_length) |
| { |
| if (tag_length == 8 || tag_length == 12 || tag_length == 16) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| static bool _isValidKeyLength(size_t length) |
| { |
| // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 |
| if (length == 16 || length == 32) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| static void _logSSLError() |
| { |
| int ssl_err_code = ERR_get_error(); |
| while (ssl_err_code != 0) |
| { |
| const char * err_str_lib = ERR_lib_error_string(ssl_err_code); |
| const char * err_str_routine = ERR_func_error_string(ssl_err_code); |
| const char * err_str_reason = ERR_reason_error_string(ssl_err_code); |
| if (err_str_lib) |
| { |
| ChipLogError(Crypto, " ssl err %s %s %s\n", err_str_lib, err_str_routine, err_str_reason); |
| } |
| ssl_err_code = ERR_get_error(); |
| } |
| } |
| |
| static const EVP_MD * _digestForType(DigestType digestType) |
| { |
| switch (digestType) |
| { |
| case DigestType::SHA256: |
| return EVP_sha256(); |
| break; |
| |
| default: |
| return NULL; |
| break; |
| } |
| } |
| |
| CHIP_ERROR chip::Crypto::AES_CCM_encrypt(const unsigned char * plaintext, size_t plaintext_length, const unsigned char * aad, |
| size_t aad_length, const unsigned char * key, size_t key_length, const unsigned char * iv, |
| size_t iv_length, unsigned char * ciphertext, unsigned char * tag, size_t tag_length) |
| { |
| EVP_CIPHER_CTX * context = NULL; |
| int bytesWritten = 0; |
| size_t ciphertext_length = 0; |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int result = 1; |
| const EVP_CIPHER * type = NULL; |
| |
| VerifyOrExit(plaintext != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(plaintext_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(iv != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(iv_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(tag != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // 16 bytes key for AES-CCM-128 |
| type = (key_length == 16) ? EVP_aes_128_ccm() : EVP_aes_256_ccm(); |
| |
| context = EVP_CIPHER_CTX_new(); |
| VerifyOrExit(context != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in cipher |
| result = EVP_EncryptInit_ex(context, type, NULL, NULL, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in IV length |
| result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_IVLEN, iv_length, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in tag length |
| result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_TAG, tag_length, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in key + iv |
| result = EVP_EncryptInit_ex(context, NULL, NULL, key, iv); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in plain text length |
| result = EVP_EncryptUpdate(context, NULL, &bytesWritten, NULL, plaintext_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in AAD |
| if (aad_length > 0 && aad != NULL) |
| { |
| result = EVP_EncryptUpdate(context, NULL, &bytesWritten, aad, aad_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| } |
| |
| // Encrypt |
| result = EVP_EncryptUpdate(context, ciphertext, &bytesWritten, plaintext, plaintext_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| ciphertext_length = bytesWritten; |
| |
| // Finalize encryption |
| result = EVP_EncryptFinal_ex(context, ciphertext + ciphertext_length, &bytesWritten); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| ciphertext_length += bytesWritten; |
| |
| // Get tag |
| result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_GET_TAG, tag_length, tag); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| exit: |
| if (context != NULL) |
| { |
| EVP_CIPHER_CTX_free(context); |
| context = NULL; |
| } |
| |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::AES_CCM_decrypt(const unsigned char * ciphertext, size_t ciphertext_length, const unsigned char * aad, |
| size_t aad_length, const unsigned char * tag, size_t tag_length, const unsigned char * key, |
| size_t key_length, const unsigned char * iv, size_t iv_length, unsigned char * plaintext) |
| { |
| EVP_CIPHER_CTX * context = NULL; |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int bytesOutput = 0; |
| int result = 1; |
| const EVP_CIPHER * type = NULL; |
| |
| VerifyOrExit(ciphertext != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(ciphertext_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(tag != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(iv != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(iv_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // 16 bytes key for AES-CCM-128 |
| type = (key_length == 16) ? EVP_aes_128_ccm() : EVP_aes_256_ccm(); |
| |
| context = EVP_CIPHER_CTX_new(); |
| VerifyOrExit(context != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in cipher |
| result = EVP_DecryptInit_ex(context, type, NULL, NULL, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in IV length |
| result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_IVLEN, iv_length, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in expected tag |
| result = EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_CCM_SET_TAG, tag_length, (void *) tag); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in key + iv |
| result = EVP_DecryptInit_ex(context, NULL, NULL, key, iv); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in cipher text length |
| result = EVP_DecryptUpdate(context, NULL, &bytesOutput, NULL, ciphertext_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Pass in aad |
| if (aad_length > 0 && aad != NULL) |
| { |
| result = EVP_DecryptUpdate(context, NULL, &bytesOutput, aad, aad_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| } |
| |
| // Pass in ciphertext. We wont get anything if validation fails. |
| result = EVP_DecryptUpdate(context, plaintext, &bytesOutput, ciphertext, ciphertext_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| exit: |
| if (context != NULL) |
| { |
| EVP_CIPHER_CTX_free(context); |
| context = NULL; |
| } |
| |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::HKDF_SHA256(const unsigned char * secret, const size_t secret_length, const unsigned char * salt, |
| const size_t salt_length, const unsigned char * info, const size_t info_length, |
| unsigned char * out_buffer, size_t out_length) |
| { |
| EVP_PKEY_CTX * context; |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int result = 1; |
| |
| context = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); |
| VerifyOrExit(context != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| VerifyOrExit(secret != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(secret_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // Salt is optional |
| if (salt_length > 0) |
| { |
| VerifyOrExit(salt != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| VerifyOrExit(info_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(info != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_buffer != NULL, 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); |
| |
| result = EVP_PKEY_CTX_set1_hkdf_key(context, secret, secret_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| if (salt_length > 0 && salt != NULL) |
| { |
| result = EVP_PKEY_CTX_set1_hkdf_salt(context, salt, salt_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| } |
| |
| if (info_length > 0 && info != NULL) |
| { |
| result = EVP_PKEY_CTX_add1_hkdf_info(context, info, 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, out_buffer, &out_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| exit: |
| if (context != NULL) |
| { |
| EVP_PKEY_CTX_free(context); |
| } |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::DRBG_get_bytes(unsigned char * out_buffer, const size_t out_length) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int result = 0; |
| |
| VerifyOrExit(out_buffer != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| result = RAND_priv_bytes(out_buffer, out_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| exit: |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::ECDSA_sign_msg(const unsigned char * msg, const size_t msg_length, const unsigned char * private_key, |
| const size_t private_key_length, unsigned char * out_signature, |
| size_t & out_signature_length) |
| { |
| ERR_clear_error(); |
| |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int result = 0; |
| EVP_MD_CTX * context = NULL; |
| int nid = NID_undef; |
| EC_KEY * ec_key = NULL; |
| EVP_PKEY * signing_key = NULL; |
| char * _hexKey = NULL; |
| BIGNUM * pvt_key = NULL; |
| const EVP_MD * md = NULL; |
| ECName curve_name = ECName::P256v1; |
| DigestType digest = DigestType::SHA256; |
| size_t out_length = 0; |
| |
| VerifyOrExit(msg != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(msg_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| nid = _nidForCurve(curve_name); |
| VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(private_key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(private_key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_signature != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| md = _digestForType(digest); |
| VerifyOrExit(md != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ec_key = EC_KEY_new_by_curve_name(nid); |
| VerifyOrExit(ec_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| pvt_key = BN_bin2bn(private_key, private_key_length, pvt_key); |
| VerifyOrExit(pvt_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EC_KEY_set_private_key(ec_key, pvt_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| signing_key = EVP_PKEY_new(); |
| VerifyOrExit(signing_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_PKEY_set1_EC_KEY(signing_key, ec_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| context = EVP_MD_CTX_create(); |
| VerifyOrExit(context != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_DigestSignInit(context, NULL, md, NULL, signing_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_DigestSignUpdate(context, msg, msg_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| // Call the EVP_DigestSignFinal with a NULL param to get length of the signature. |
| |
| result = EVP_DigestSignFinal(context, NULL, &out_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| VerifyOrExit(out_signature_length >= out_length, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| result = EVP_DigestSignFinal(context, out_signature, &out_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| // This should not happen due to the check above. But check this nonetheless |
| VerifyOrExit(out_signature_length >= out_length, error = CHIP_ERROR_INTERNAL); |
| out_signature_length = out_length; |
| |
| exit: |
| if (ec_key != NULL) |
| { |
| EC_KEY_free(ec_key); |
| ec_key = NULL; |
| } |
| |
| if (context != NULL) |
| { |
| EVP_MD_CTX_destroy(context); |
| context = NULL; |
| } |
| if (signing_key != NULL) |
| { |
| EVP_PKEY_free(signing_key); |
| signing_key = NULL; |
| } |
| |
| if (error != CHIP_NO_ERROR) |
| { |
| _logSSLError(); |
| } |
| |
| if (_hexKey != NULL) |
| { |
| free(_hexKey); |
| } |
| |
| if (pvt_key != NULL) |
| { |
| BN_free(pvt_key); |
| } |
| |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::ECDSA_validate_msg_signature(const unsigned char * msg, const size_t msg_length, |
| const unsigned char * public_key, const size_t public_key_length, |
| const unsigned char * signature, const size_t signature_length) |
| { |
| ERR_clear_error(); |
| CHIP_ERROR error = CHIP_ERROR_INTERNAL; |
| int nid = NID_undef; |
| const EVP_MD * md = NULL; |
| EC_KEY * ec_key = NULL; |
| EVP_PKEY * verification_key = NULL; |
| EC_POINT * key_point = NULL; |
| EC_GROUP * ec_group = NULL; |
| int result = 0; |
| EVP_MD_CTX * md_context = NULL; |
| ECName curve_name = ECName::P256v1; |
| DigestType digest = DigestType::SHA256; |
| |
| VerifyOrExit(msg != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(msg_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| nid = _nidForCurve(curve_name); |
| VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(public_key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(public_key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(signature != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(signature_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| md = _digestForType(digest); |
| VerifyOrExit(md != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ec_group = EC_GROUP_new_by_curve_name(nid); |
| VerifyOrExit(ec_group != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| key_point = EC_POINT_new(ec_group); |
| VerifyOrExit(key_point != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EC_POINT_oct2point(ec_group, key_point, public_key, public_key_length, NULL); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| ec_key = EC_KEY_new_by_curve_name(nid); |
| VerifyOrExit(ec_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| 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); |
| |
| verification_key = EVP_PKEY_new(); |
| VerifyOrExit(verification_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_PKEY_set1_EC_KEY(verification_key, ec_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| md_context = EVP_MD_CTX_create(); |
| VerifyOrExit(md_context != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_DigestVerifyInit(md_context, NULL, md, NULL, verification_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_DigestVerifyUpdate(md_context, msg, msg_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_DigestVerifyFinal(md_context, signature, signature_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INVALID_SIGNATURE); |
| error = CHIP_NO_ERROR; |
| |
| exit: |
| _logSSLError(); |
| if (ec_group != NULL) |
| { |
| EC_GROUP_free(ec_group); |
| ec_group = NULL; |
| } |
| if (key_point != NULL) |
| { |
| EC_POINT_clear_free(key_point); |
| key_point = NULL; |
| } |
| if (md_context) |
| { |
| EVP_MD_CTX_destroy(md_context); |
| md_context = NULL; |
| } |
| if (ec_key != NULL) |
| { |
| EC_KEY_free(ec_key); |
| ec_key = NULL; |
| } |
| if (verification_key != NULL) |
| { |
| EVP_PKEY_free(verification_key); |
| verification_key = NULL; |
| } |
| 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 unsigned char * key, const size_t key_length, EVP_PKEY ** out_evp_pkey, |
| bool isPrivateKey) |
| { |
| |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| BIGNUM * big_num_key = NULL; |
| EC_KEY * ec_key = NULL; |
| int result = -1; |
| EC_POINT * point = NULL; |
| EC_GROUP * group = NULL; |
| int nid = NID_undef; |
| |
| VerifyOrExit(key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(*out_evp_pkey == NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| nid = _nidForCurve(ECName::P256v1); |
| VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INTERNAL); |
| |
| ec_key = EC_KEY_new_by_curve_name(nid); |
| VerifyOrExit(ec_key != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| big_num_key = BN_bin2bn(key, key_length, NULL); |
| VerifyOrExit(big_num_key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| if (isPrivateKey) |
| { |
| result = EC_KEY_set_private_key(ec_key, big_num_key); |
| } |
| else |
| { |
| group = EC_GROUP_new_by_curve_name(nid); |
| VerifyOrExit(group != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| point = EC_POINT_new(group); |
| VerifyOrExit(point != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EC_POINT_oct2point(group, point, key, key_length, NULL); |
| 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 != NULL, error = CHIP_ERROR_INTERNAL); |
| |
| result = EVP_PKEY_set1_EC_KEY(*out_evp_pkey, ec_key); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| |
| exit: |
| if (big_num_key) |
| { |
| BN_free(big_num_key); |
| big_num_key = NULL; |
| } |
| |
| if (ec_key != NULL) |
| { |
| EC_KEY_free(ec_key); |
| ec_key = NULL; |
| } |
| |
| if (error != CHIP_NO_ERROR && *out_evp_pkey) |
| { |
| EVP_PKEY_free(*out_evp_pkey); |
| out_evp_pkey = NULL; |
| } |
| |
| if (point != NULL) |
| { |
| EC_POINT_free(point); |
| point = NULL; |
| } |
| |
| if (group != NULL) |
| { |
| EC_GROUP_free(group); |
| group = NULL; |
| } |
| |
| return error; |
| } |
| |
| CHIP_ERROR chip::Crypto::ECDH_derive_secret(const unsigned char * remote_public_key, const size_t remote_public_key_length, |
| const unsigned char * local_private_key, const size_t local_private_key_length, |
| unsigned char * out_secret, size_t & out_secret_length) |
| { |
| ERR_clear_error(); |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| int result = -1; |
| EVP_PKEY * local_key = NULL; |
| EVP_PKEY * remote_key = NULL; |
| |
| EVP_PKEY_CTX * context = NULL; |
| size_t out_buf_length = 0; |
| |
| VerifyOrExit(remote_public_key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(remote_public_key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(local_private_key != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(local_private_key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_secret != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(out_secret_length >= kMax_ECDH_Secret_Length, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| error = _create_evp_key_from_binary_p256_key(local_private_key, local_private_key_length, &local_key, true); |
| SuccessOrExit(error); |
| |
| error = _create_evp_key_from_binary_p256_key(remote_public_key, remote_public_key_length, &remote_key, false); |
| SuccessOrExit(error); |
| |
| context = EVP_PKEY_CTX_new(local_key, NULL); |
| VerifyOrExit(context != NULL, 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; |
| result = EVP_PKEY_derive(context, out_secret, &out_buf_length); |
| VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); |
| VerifyOrExit(out_secret_length >= out_buf_length, error = CHIP_ERROR_INTERNAL); |
| out_secret_length = out_buf_length; |
| |
| exit: |
| if (local_key != NULL) |
| { |
| EVP_PKEY_free(local_key); |
| local_key = NULL; |
| } |
| |
| if (remote_key != NULL) |
| { |
| EVP_PKEY_free(remote_key); |
| remote_key = NULL; |
| } |
| |
| if (context != NULL) |
| { |
| EVP_PKEY_CTX_free(context); |
| context = NULL; |
| } |
| |
| _logSSLError(); |
| return error; |
| } |