blob: 320d63fac42ea8d8d1804535eb2030d4c479b7f4 [file] [log] [blame]
/*
*
* Copyright (c) 2025 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 "CHIPCryptoPALOpenSSL.h"
#include <type_traits>
#if CHIP_CRYPTO_BORINGSSL
#include <openssl/aead.h>
#endif // CHIP_CRYPTO_BORINGSSL
#include <openssl/err.h>
#include <openssl/evp.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/BufferWriter.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CHIPArgParser.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
namespace chip {
namespace Crypto {
static inline void from_EC_KEY(EC_KEY * key, P256KeypairContext * context)
{
*SafePointerCast<EC_KEY **>(context) = key;
}
static inline EC_KEY * to_EC_KEY(P256KeypairContext * context)
{
return *SafePointerCast<EC_KEY **>(context);
}
static inline const EC_KEY * to_const_EC_KEY(const P256KeypairContext * context)
{
return *SafePointerCast<const EC_KEY * const *>(context);
}
// 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 = GetNidForCurve(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::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 = GetNidForCurve(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<boringssl_size_t_openssl_int>(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<size_t>(BN_num_bytes(r)) && CanCastTo<size_t>(BN_num_bytes(s)), error = CHIP_ERROR_INTERNAL);
VerifyOrExit((static_cast<size_t>(BN_num_bytes(r)) <= kP256_FE_Length) &&
(static_cast<size_t>(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)
{
SSLErrorLog();
}
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;
}
SSLErrorLog();
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 = GetNidForCurve(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;
}
SSLErrorLog();
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);
TEMPORARY_RETURN_IGNORED output.SetLength(bbuf.Needed());
}
exit:
ClearSecretData(privkey, sizeof(privkey));
SSLErrorLog();
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 = GetNidForCurve(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;
}
SSLErrorLog();
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<size_t>(csr_length_local), error = CHIP_ERROR_BUFFER_TOO_SMALL);
VerifyOrExit(static_cast<size_t>(csr_length_local) <= csr_length, error = CHIP_ERROR_BUFFER_TOO_SMALL);
csr_length = static_cast<size_t>(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);
SSLErrorLog();
return error;
}
} // namespace Crypto
} // namespace chip