blob: 8d5aa35182e41d41fdba35a85ee7fe583f42ad4d [file]
/*
*
* 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 <limits>
#include <type_traits>
#if !CHIP_CRYPTO_BORINGSSL && defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30200000L
#include <openssl/core_names.h>
#include <openssl/params.h>
#elif CHIP_CRYPTO_BORINGSSL
#include <openssl/aead.h>
#endif
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/BufferWriter.h>
#include <lib/support/BytesToHex.h>
#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;
}
// Encode an ECDSA_SIG (r, s) pair as a raw P256ECDSASignature (r || s, each zero-padded to kP256_FE_Length).
static CHIP_ERROR encode_signature(const ECDSA_SIG * sig, P256ECDSASignature & out_signature)
{
const BIGNUM * r = nullptr;
const BIGNUM * s = nullptr;
ECDSA_SIG_get0(sig, &r, &s); // sig keeps ownership of r and s
VerifyOrReturnError((r != nullptr) && (s != nullptr), CHIP_ERROR_INTERNAL);
VerifyOrReturnError(BN_bn2binpad(r, out_signature.Bytes(), kP256_FE_Length) == kP256_FE_Length, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(BN_bn2binpad(s, out_signature.Bytes() + kP256_FE_Length, kP256_FE_Length) == kP256_FE_Length,
CHIP_ERROR_INTERNAL);
return out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw);
}
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;
VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT);
uint8_t digest[kSHA256_Hash_Length];
SHA256(msg, msg_length, digest);
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);
error = encode_signature(sig, out_signature);
exit:
if (sig != nullptr)
{
ECDSA_SIG_free(sig);
}
if (error != CHIP_NO_ERROR)
{
SSLErrorLog();
}
return error;
}
#if !CHIP_CRYPTO_BORINGSSL && defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30200000L
// OpenSSL 3.2+ natively supports deterministic ECDSA (RFC 6979) via the EVP signing API.
CHIP_ERROR P256Keypair::ECDSA_sign_msg_det(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const
{
VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED);
ERR_clear_error();
CHIP_ERROR error = CHIP_NO_ERROR;
EVP_MD_CTX * md_ctx = nullptr;
EVP_PKEY * evp_pkey = nullptr;
EC_KEY * ec_key = nullptr;
ec_key = EC_KEY_dup(to_const_EC_KEY(&mKeypair));
VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL);
evp_pkey = EVP_PKEY_new();
VerifyOrExit(evp_pkey != nullptr, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(EVP_PKEY_set1_EC_KEY(evp_pkey, ec_key) == 1, error = CHIP_ERROR_INTERNAL);
md_ctx = EVP_MD_CTX_new();
VerifyOrExit(md_ctx != nullptr, error = CHIP_ERROR_INTERNAL);
{
// EVP_DigestSignInit sets pkey_ctx; it is owned by md_ctx and must not be freed separately.
EVP_PKEY_CTX * pkey_ctx = nullptr;
VerifyOrExit(EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), nullptr, evp_pkey) == 1, error = CHIP_ERROR_INTERNAL);
// Request deterministic nonce generation (RFC 6979).
unsigned int nonce_type = 1;
OSSL_PARAM params[] = {
OSSL_PARAM_construct_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, &nonce_type),
OSSL_PARAM_END,
};
VerifyOrExit(EVP_PKEY_CTX_set_params(pkey_ctx, params) == 1, error = CHIP_ERROR_INTERNAL);
}
// EVP_DigestSign produces a DER-encoded ECDSA signature; decode it to raw (r || s).
{
uint8_t der_sig[kP256_ECDSA_Signature_Length_Raw + 16]; // DER overhead is at most ~8 bytes for P-256
size_t der_sig_len = sizeof(der_sig);
VerifyOrExit(EVP_DigestSign(md_ctx, der_sig, &der_sig_len, msg, msg_length) == 1, error = CHIP_ERROR_INTERNAL);
const uint8_t * p = der_sig;
ECDSA_SIG * sig = d2i_ECDSA_SIG(nullptr, &p, static_cast<long>(der_sig_len));
VerifyOrExit(sig != nullptr, error = CHIP_ERROR_INTERNAL);
error = encode_signature(sig, out_signature);
ECDSA_SIG_free(sig);
}
exit:
if (md_ctx != nullptr)
{
EVP_MD_CTX_free(md_ctx);
}
if (evp_pkey != nullptr)
{
EVP_PKEY_free(evp_pkey);
}
if (ec_key != nullptr)
{
EC_KEY_free(ec_key);
}
if (error != CHIP_NO_ERROR)
{
SSLErrorLog();
}
return error;
}
#elif CHIP_CRYPTO_BORINGSSL
// BoringSSL removed RFC 6979 support, so we implement it manually:
// derive k per RFC 6979 section 3.2, then sign with an explicit nonce.
// Constant-time helper: returns (v != 0) ? ~0 : 0
static inline unsigned int ct_nonzero_mask(unsigned int v)
{
return -((v | -v) >> (std::numeric_limits<decltype(v)>::digits - 1));
}
// Constant-time buffer comparison: returns (a >= b) ? ~0 : 0
// where a and b are interpreted as big-endian unsigned integers.
static unsigned int ct_buffer_gte(const volatile uint8_t * a, const volatile uint8_t * b, size_t n)
{
// Loosely based on mbedtls_ct_memcmp() from third_party/mbedtls/repo/library/constant_time.c
unsigned int diff = 0;
for (size_t i = 0; i < n; i++)
{
unsigned int x = a[i], y = b[i];
diff |= ~ct_nonzero_mask(diff) & (x - y);
}
// If a < b, the first byte difference will be negative, i.e. have MSB == 1,
// so extract the MSB, spread it to all bits via unary minus, and invert.
return ~-(diff >> (std::numeric_limits<decltype(diff)>::digits - 1));
}
// HMAC_K(V || sep || x || h1_reduced) -> K, used in RFC 6979 steps d and f.
static bool rfc6979_hmac_update_kv(uint8_t K[kSHA256_Hash_Length], const uint8_t V[kSHA256_Hash_Length], uint8_t sep,
const uint8_t x[kP256_FE_Length], const uint8_t h1_reduced[kP256_FE_Length])
{
HMAC_CTX * ctx = HMAC_CTX_new();
VerifyOrReturnValue(ctx != nullptr, false);
unsigned int len = kSHA256_Hash_Length;
bool ok = HMAC_Init_ex(ctx, K, kSHA256_Hash_Length, EVP_sha256(), nullptr) && //
HMAC_Update(ctx, V, kSHA256_Hash_Length) && //
HMAC_Update(ctx, &sep, 1) && //
HMAC_Update(ctx, x, kP256_FE_Length) && //
HMAC_Update(ctx, h1_reduced, kP256_FE_Length) && //
HMAC_Final(ctx, K, &len);
HMAC_CTX_free(ctx);
return ok;
}
// RFC 6979 section 3.2 - Deterministic k generation for ECDSA with HMAC-SHA256.
// For P-256 with SHA-256, qlen == hlen == 256 bits, so int2octets is the identity
// on 32-byte values. bits2octets requires reducing the hash modulo the group order.
//
// Derives a valid nonce k in [1, order) and writes it as 32 big-endian bytes to out_k.
// The private key x and hash h1 must each be exactly kP256_FE_Length bytes.
static CHIP_ERROR rfc6979_derive_k(const EC_KEY * ec_key, const uint8_t x[kP256_FE_Length], const uint8_t h1[kSHA256_Hash_Length],
uint8_t out_k[kP256_FE_Length])
{
unsigned int success = 0;
CHIP_ERROR error = CHIP_NO_ERROR;
memset(out_k, 0x00, kP256_FE_Length);
// Serialize the group order for constant-time comparison in the candidate loop below.
uint8_t order[kP256_FE_Length];
const EC_GROUP * group = EC_KEY_get0_group(ec_key);
const BIGNUM * order_bn = EC_GROUP_get0_order(group);
VerifyOrReturnError(group != nullptr && order_bn != nullptr, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(BN_bn2binpad(order_bn, order, sizeof(order)) == sizeof(order), CHIP_ERROR_INTERNAL);
BN_CTX * bn_ctx = BN_CTX_new();
VerifyOrReturnError(bn_ctx != nullptr, CHIP_ERROR_INTERNAL);
BN_CTX_start(bn_ctx);
// bits2octets(h1): reduce hash modulo the group order
uint8_t h1_reduced[kP256_FE_Length];
{
BIGNUM * h1_bn = BN_CTX_get(bn_ctx);
VerifyOrExit(h1_bn != nullptr, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(BN_bin2bn(h1, kSHA256_Hash_Length, h1_bn) != nullptr, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(BN_nnmod(h1_bn, h1_bn, order_bn, bn_ctx) == 1, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(BN_bn2binpad(h1_bn, h1_reduced, sizeof(h1_reduced)) == sizeof(h1_reduced), error = CHIP_ERROR_INTERNAL);
}
// Steps b-c: initialize V and K
uint8_t V[kSHA256_Hash_Length];
memset(V, 0x01, sizeof(V));
uint8_t K[kSHA256_Hash_Length];
memset(K, 0x00, sizeof(K));
// Step d: K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1))
VerifyOrExit(rfc6979_hmac_update_kv(K, V, 0x00, x, h1_reduced), error = CHIP_ERROR_INTERNAL);
// Step e: V = HMAC_K(V)
VerifyOrExit(HMAC(EVP_sha256(), K, sizeof(K), V, sizeof(V), V, nullptr) != nullptr, error = CHIP_ERROR_INTERNAL);
// Step f: K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1))
VerifyOrExit(rfc6979_hmac_update_kv(K, V, 0x01, x, h1_reduced), error = CHIP_ERROR_INTERNAL);
// Step g: V = HMAC_K(V)
VerifyOrExit(HMAC(EVP_sha256(), K, sizeof(K), V, sizeof(V), V, nullptr) != nullptr, error = CHIP_ERROR_INTERNAL);
// Step h: Generate candidate k, retrying if k is not in [1, order).
// For P-256/SHA-256, one HMAC output (256 bits) == qlen, so one iteration
// of the inner loop suffices to produce a candidate. bits2int is the identity
// since qlen == hlen.
// This logic needs to be constant-time to avoid leaking information about k
// from either the numeric comparison operation to the order, or via the
// number of candidates that had to be tested. The probability of rejection is
// about 1 in 2^32 per round, so with our limit of 8 rounds, the chance of
// failure is approximately 1 in 2^256 (10^77).
for (int attempt = 0; attempt < 8; attempt++)
{
// Generate candidate: V = HMAC_K(V)
VerifyOrExit(HMAC(EVP_sha256(), K, sizeof(K), V, sizeof(V), V, nullptr) != nullptr, error = CHIP_ERROR_INTERNAL);
// V is our result if it's in range [1, order) and we weren't already successful.
// This is equivalent to !success && !(0 >= V) && !(V >= order)
// Instead of a conditional memcpy(out_k, V) bitwise or everything together with the result mask.
constexpr auto zero = FixedByteSpan<kP256_FE_Length>().data(); // shared buffer of zeroes
unsigned int result = ~success & ~ct_buffer_gte(zero, V, kP256_FE_Length) & ~ct_buffer_gte(V, order, kP256_FE_Length);
for (size_t b = 0; b < kP256_FE_Length; b++)
{
out_k[b] |= V[b] & result;
}
success |= result;
// RFC 6979 step h.3: K = HMAC_K(V || 0x00), V = HMAC_K(V)
{
HMAC_CTX * ctx = HMAC_CTX_new();
VerifyOrExit(ctx != nullptr, error = CHIP_ERROR_INTERNAL);
unsigned int len = sizeof(K);
uint8_t sep = 0x00;
bool ok = HMAC_Init_ex(ctx, K, sizeof(K), EVP_sha256(), nullptr) && //
HMAC_Update(ctx, V, sizeof(V)) && //
HMAC_Update(ctx, &sep, 1) && //
HMAC_Final(ctx, K, &len);
HMAC_CTX_free(ctx);
VerifyOrExit(ok, error = CHIP_ERROR_INTERNAL);
}
VerifyOrExit(HMAC(EVP_sha256(), K, sizeof(K), V, sizeof(V), V, nullptr) != nullptr, error = CHIP_ERROR_INTERNAL);
}
VerifyOrExit(success, error = CHIP_ERROR_INTERNAL);
exit:
ClearSecretData(V);
ClearSecretData(K);
ClearSecretData(h1_reduced);
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return error;
}
CHIP_ERROR P256Keypair::ECDSA_sign_msg_det(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const
{
VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED);
ERR_clear_error();
CHIP_ERROR error = CHIP_NO_ERROR;
ECDSA_SIG * sig = nullptr;
// Step a: h1 = Hash(msg)
uint8_t h1[kSHA256_Hash_Length];
SHA256(msg, msg_length, h1);
const EC_KEY * ec_key = to_const_EC_KEY(&mKeypair);
VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL);
// Extract private key as big-endian bytes: int2octets(x)
uint8_t x[kP256_FE_Length];
{
const BIGNUM * privkey_bn = EC_KEY_get0_private_key(ec_key);
VerifyOrExit(privkey_bn != nullptr, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(BN_bn2binpad(privkey_bn, x, sizeof(x)) == sizeof(x), error = CHIP_ERROR_INTERNAL);
}
// Derive deterministic nonce k via RFC 6979
uint8_t k[kP256_FE_Length];
SuccessOrExit((error = rfc6979_derive_k(ec_key, x, h1, k)));
// Sign using the deterministic nonce k derived above.
// Despite its alarming name, ECDSA_sign_with_nonce_and_leak_private_key_for_testing is just a
// standard ECDSA sign that accepts a caller-supplied nonce instead of generating one internally.
// This testing hook is the only way to supply a pre-computed nonce. The "leak_private_key" risk
// it guards against are scenarios where a caller reuses a nonce; here we provide a nonce that is
// securely derived from the private key and message as per RFC 6979.
sig = ECDSA_sign_with_nonce_and_leak_private_key_for_testing(h1, sizeof(h1), ec_key, k, sizeof(k));
VerifyOrExit(sig != nullptr, error = CHIP_ERROR_INTERNAL);
error = encode_signature(sig, out_signature);
exit:
ClearSecretData(x);
ClearSecretData(k);
if (sig != nullptr)
{
ECDSA_SIG_free(sig);
}
if (error != CHIP_NO_ERROR)
{
SSLErrorLog();
}
return error;
}
#endif // CHIP_CRYPTO_BORINGSSL || OPENSSL_VERSION_NUMBER >= 0x30200000L
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);
SuccessOrExit(error = 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_clear_free(pvt_key); // wipe!
pvt_key = nullptr;
}
if (key_point != nullptr)
{
EC_POINT_free(key_point);
key_point = nullptr;
}
SSLErrorLog();
return error;
}
CHIP_ERROR P256Keypair::InitializeFromBitsOrReject(FixedByteSpan<kP256_PrivateKey_Length> privateKeyBits)
{
ERR_clear_error();
Clear();
const EC_GROUP * group;
const BIGNUM * order;
BIGNUM * pvt_key = nullptr;
EC_POINT * pub_point = nullptr;
EC_KEY * ec_key = nullptr;
CHIP_ERROR error = CHIP_NO_ERROR;
int result = 0;
int nid = GetNidForCurve(MapECName(mPublicKey.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_KEY_get0_group(ec_key);
order = EC_GROUP_get0_order(group);
// Convert private key bits to BIGNUM x, then compute d = x + 1.
// Checking that d < order ensures that x was in [0, order - 2].
pvt_key = BN_bin2bn(privateKeyBits.data(), privateKeyBits.size(), nullptr);
VerifyOrExit(pvt_key != nullptr, error = CHIP_ERROR_INTERNAL);
result = BN_add(pvt_key, pvt_key, BN_value_one());
VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(BN_cmp(pvt_key, order) < 0, error = CHIP_ERROR_INVALID_ARGUMENT);
// Compute public key: Q = d * G
pub_point = EC_POINT_new(group);
VerifyOrExit(pub_point != nullptr, error = CHIP_ERROR_INTERNAL);
result = EC_POINT_mul(group, pub_point, pvt_key, nullptr, nullptr, nullptr);
VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL);
result = EC_KEY_set_private_key(ec_key, pvt_key);
VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL);
result = EC_KEY_set_public_key(ec_key, pub_point);
VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL);
SuccessOrExit(error = P256PublicKeyFromECKey(ec_key, mPublicKey));
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 (pub_point != nullptr)
{
EC_POINT_free(pub_point);
pub_point = nullptr;
}
if (pvt_key != nullptr)
{
BN_clear_free(pvt_key); // wipe!
pvt_key = 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