Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restyle Missing openSSL based ECDH implementation #420 #521

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"name": "CHIP openSSL Tests",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/default/src/crypto/tests/TestOpenSSLCrypto",
"program": "${workspaceFolder}/build/default/src/crypto/tests/TestCryptoPAL",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/openssl_tests.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

make -C build/default/src/crypto/ && make -C build/default/src/crypto/tests/ check
make -C build/default/src/crypto/ && make -C build/default/src/crypto/tests/ TestCryptoPAL
17 changes: 17 additions & 0 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace chip {
namespace Crypto {

const size_t kMax_ECDSA_Signature_Length = 72;
const size_t kMax_ECDH_Secret_Length = 64;

/**
* @brief A function that implements 256-bit AES-CCM encryption
Expand Down Expand Up @@ -162,6 +163,22 @@ CHIP_ERROR ECDSA_sign_msg(const unsigned char * msg, const size_t msg_length, co
CHIP_ERROR 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);

/** @brief A function to derive a shared secret using ECDH
* @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is
*ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography
*[https://www.secg.org/sec1-v2.pdf]
* @param remote_public_key_length Length of remote_public_key
* @param local_private_key Local private key. local_private_key is ASN.1 DER encoded as padded big-endian field elements as
*described in SEC 1: Elliptic Curve Cryptography [https://www.secg.org/sec1-v2.pdf]
* @param local_private_key_length Length of private_key_length
* @param out_secret Buffer to write out secret into. This is a byte array representing the x coordinate of the shared secret.
* @param out_secret_length Length of out_secret
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR 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);
} // namespace Crypto
} // namespace chip

Expand Down
159 changes: 158 additions & 1 deletion src/crypto/CHIPCryptoPALOpenSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ enum class ECName

using namespace chip::Crypto;

static_assert(kMax_ECDH_Secret_Length >= 64, "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)
Expand Down Expand Up @@ -331,7 +334,6 @@ CHIP_ERROR chip::Crypto::ECDSA_sign_msg(const unsigned char * msg, const size_t
size_t & out_signature_length)
{
ERR_clear_error();
static_assert(kMax_ECDSA_Signature_Length >= 72, "ECDSA signature buffer length is too short");

CHIP_ERROR error = CHIP_NO_ERROR;
int result = 0;
Expand Down Expand Up @@ -420,6 +422,11 @@ CHIP_ERROR chip::Crypto::ECDSA_sign_msg(const unsigned char * msg, const size_t
free(_hexKey);
}

if (pvt_key != NULL)
{
BN_free(pvt_key);
}

return error;
}

Expand Down Expand Up @@ -518,3 +525,153 @@ CHIP_ERROR chip::Crypto::ECDSA_validate_msg_signature(const unsigned char * msg,
}
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;
}
105 changes: 105 additions & 0 deletions src/crypto/tests/CHIPCryptoPALTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "CHIPCryptoPAL.h"
#include "AES_CCM_256_test_vectors.h"
#include "ECDH_P256_test_vectors.h"
#include "HKDF_SHA256_test_vectors.h"

#include <nlunit-test.h>
Expand Down Expand Up @@ -460,6 +461,107 @@ static void TestECDSA_ValidationInvalidParam(nlTestSuite * inSuite, void * inCon
validation_error = CHIP_NO_ERROR;
}

static void TestECDH_EstablishSecret(nlTestSuite * inSuite, void * inContext)
{
unsigned char private_key1[] = {
0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f, 0x11, 0x0e, 0x34, 0x15, 0x81,
0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65, 0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1
};

const unsigned char public_key1[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4,
0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7,
0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79,
0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf,
0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 };

const unsigned char private_key2[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47,
0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2,
0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 };

const unsigned char public_key2[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f,
0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6,
0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd,
0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae,
0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 };

unsigned char out_secret1[kMax_ECDH_Secret_Length] = { 0 };
size_t out_size1 = sizeof(out_secret1);

unsigned char out_secret2[kMax_ECDH_Secret_Length] = { 1 };
size_t out_size2 = sizeof(out_secret2);
CHIP_ERROR error = CHIP_NO_ERROR;
NL_TEST_ASSERT(inSuite, memcmp(out_secret1, out_secret2, out_size1) != 0); // Validate that buffers are indeed different.

error = ECDH_derive_secret(public_key1, sizeof(public_key1), private_key2, sizeof(private_key2), out_secret1, out_size1);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);

error = ECDH_derive_secret(public_key2, sizeof(public_key2), private_key1, sizeof(private_key1), out_secret2, out_size2);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);

bool signature_lengths_match = out_size1 == out_size2;
NL_TEST_ASSERT(inSuite, signature_lengths_match);

bool signatures_match = (memcmp(out_secret1, out_secret2, out_size1) == 0);
NL_TEST_ASSERT(inSuite, signatures_match);
}

static void TestECDH_InvalidParams(nlTestSuite * inSuite, void * inContext)
{
unsigned char private_key1[] = {
0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f, 0x11, 0x0e, 0x34, 0x15, 0x81,
0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65, 0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1
};

const unsigned char public_key2[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f,
0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6,
0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd,
0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae,
0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 };

unsigned char out_secret[kMax_ECDH_Secret_Length] = { 0 };
size_t out_size = sizeof(out_secret);

CHIP_ERROR error = ECDH_derive_secret(NULL, sizeof(public_key2), private_key1, sizeof(private_key1), out_secret, out_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);

error = ECDH_derive_secret(public_key2, 0, private_key1, sizeof(private_key1), out_secret, out_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);

error = ECDH_derive_secret(public_key2, sizeof(public_key2), NULL, sizeof(private_key1), out_secret, out_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);

error = ECDH_derive_secret(public_key2, sizeof(public_key2), private_key1, 0, out_secret, out_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);

error = ECDH_derive_secret(public_key2, sizeof(public_key2), private_key1, sizeof(private_key1), NULL, out_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);

size_t bad_size = 0;
error = ECDH_derive_secret(public_key2, sizeof(public_key2), private_key1, sizeof(private_key1), out_secret, bad_size);
NL_TEST_ASSERT(inSuite, error == CHIP_ERROR_INVALID_ARGUMENT);
}

static void TestECDH_SampleInputVectors(nlTestSuite * inSuite, void * inContext)
{

size_t numOfTestsExecuted = 0;
for (numOfTestsExecuted = 0; numOfTestsExecuted < ArraySize(ecdh_test_vectors); numOfTestsExecuted++)
{
unsigned char out_secret[kMax_ECDH_Secret_Length] = { 0 };
size_t out_secret_length = sizeof(out_secret);
ECDH_P256_test_vector v = ecdh_test_vectors[numOfTestsExecuted];
CHIP_ERROR error = ECDH_derive_secret(v.remote_pub_key, v.remote_pub_key_length, v.local_pvt_key, v.local_pvt_key_length,
out_secret, out_secret_length);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);
if (error == CHIP_NO_ERROR)
{
int result = memcmp(out_secret, v.shared_secret, out_secret_length) == 0;
NL_TEST_ASSERT(inSuite, result == true);
}
}
NL_TEST_ASSERT(inSuite, numOfTestsExecuted > 0);
}

namespace chip {
namespace Logging {
void Log(uint8_t module, uint8_t category, const char * format, ...)
Expand Down Expand Up @@ -488,6 +590,9 @@ static const nlTest sTests[] = {
NL_TEST_DEF("Test decrypting invalid key", TestAES_CCM_256DecryptInvalidKey),
NL_TEST_DEF("Test decrypting invalid IV", TestAES_CCM_256DecryptInvalidIVLen),
NL_TEST_DEF("Test decrypting invalid vectors", TestAES_CCM_256DecryptInvalidTestVectors),
NL_TEST_DEF("Test ECDH derive shared secret", TestECDH_EstablishSecret),
NL_TEST_DEF("Test ECDH invalid params", TestECDH_InvalidParams),
NL_TEST_DEF("Test ECDH sample vectors", TestECDH_SampleInputVectors),
#endif
NL_TEST_DEF("Test ECDSA signing and validation using SHA256", TestECDSA_Signing_SHA256),
NL_TEST_DEF("Test ECDSA signature validation fail - Different msg", TestECDSA_ValidationFailsDifferentMessage),
Expand Down
Loading