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

Support for micro-ecc (WIP) #228

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
170 changes: 140 additions & 30 deletions crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@
#else
#define assert(x)
#endif
#include <inttypes.h>

#include "global.h"
#include "dtls_debug.h"
#include "numeric.h"
#include "dtls.h"
#include "crypto.h"
#include "ccm.h"
#include "ecc/micro-ecc/uECC.h"
#ifdef DTLS_ECC
#include "ext/micro-ecc/uECC.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ext/micro-ecc" is part of the include path, maybe it's simpler to use "uECC.h"?

Copy link
Contributor

@mrdeep1 mrdeep1 Feb 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is my preference to always include the package information path in the #include statement, to prevent name clashes (unlikely with uECC.h, but with possible with names like global.h etc.).

So, I would recommend that the include path includes ext/, and use #include <micro-ecc/uECC.h>.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pain is, that "micro-ecc/uECC.c" uses:

#include "uECC.h"
#include "uECC_vli.h"

and "micro-ecc/uECC_vli.h" uses:

#include "uECC.h"
#include "types.h"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything that is getting built external to micro-ecc will need the equivalent of "micro-ecc/uECC.h" to get at the function definitions. For the actual micro-ecc code compilation, I assume that it is done in the micro-ecc/ directory, in which case just "uECCH.h" without a path is fine.

If you declare #include "" , the compiler first searches in current directory of source file and if not found,
continues to search in any included -I directories and then the default include directories.

If you declare #include <> , the compiler searches directly in any included -I directories and then the default
include directories.

$ cpp -v shows the include search directory order.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I've checked it and at least the cmake build doesn't need to extend the include path with "ext/micro-ecc".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ext/micro-ecc" is part of the include path,

I mixed it up, it's only part of "FILES".

#endif /* DTLS_ECC */
#include "dtls_prng.h"
#include "netq.h"

Expand Down Expand Up @@ -356,6 +359,10 @@ dtls_psk_pre_master_secret(unsigned char *key, size_t keylen,
#endif /* DTLS_PSK */

#ifdef DTLS_ECC
#ifdef uECC_SUPPORTS_secp256r1
const dtls_ecdh_curve default_curve = TLS_EXT_ELLIPTIC_CURVES_SECP256R1;
#endif /* uECC_SUPPORTS_secp256r1 */

static void dtls_ec_key_to_uint32(const unsigned char *key, size_t key_size,
uint32_t *result) {
int i;
Expand Down Expand Up @@ -432,64 +439,148 @@ int dtls_ec_key_asn1_from_uint32(const uint32_t *key, size_t key_size,
return key_size + 2;
}

static int get_uecc_curve(dtls_ecdh_curve curve, uECC_Curve *result) {
struct {
dtls_ecdh_curve curve;
uECC_Curve uecc_curve;
} known_curves[] = {
#if uECC_SUPPORTS_secp256r1
{ TLS_EXT_ELLIPTIC_CURVES_SECP256R1, uECC_secp256r1() },
#endif /* uECC_SUPPORTS_secp256r1 */
};
unsigned int index;

for (index = 0; index < sizeof(known_curves)/sizeof(known_curves[0]); index++) {
if (known_curves[index].curve == curve) {
*result = known_curves[index].uecc_curve;
return 1;
}
}
return 0;
}

int dtls_ecdh_pre_master_secret2(const unsigned char *priv_key,
const unsigned char *pub_key,
size_t key_size,
dtls_ecdh_curve curve,
unsigned char *result,
size_t result_len) {
uECC_Curve uecc_curve;
if (!get_uecc_curve(curve, &uecc_curve)) {
dtls_warn("curve %" PRIu16 " not supported\n", curve);
return -1;
}

if (result_len < key_size) {
return -1;
}

if (!uECC_valid_public_key(pub_key, uecc_curve)) {
dtls_warn("invalid public key\n");
}

if (!uECC_shared_secret(pub_key, priv_key, result, uecc_curve)) {
dtls_warn("cannot generate ECDH shared secret\n");
return 0;
}

return key_size;
}

int dtls_ecdh_pre_master_secret(unsigned char *priv_key,
unsigned char *pub_key_x,
unsigned char *pub_key_y,
size_t key_size,
unsigned char *result,
size_t result_len) {
const dtls_ecdh_curve curve = default_curve;
uint8_t pub_key[2 * DTLS_EC_KEY_SIZE];
uint8_t priv[DTLS_EC_KEY_SIZE];

if (result_len < key_size) {
return -1;
}

memcpy(priv, priv_key, DTLS_EC_KEY_SIZE);
memcpy(pub_key, pub_key_x, DTLS_EC_KEY_SIZE);
memcpy(pub_key + DTLS_EC_KEY_SIZE, pub_key_y, DTLS_EC_KEY_SIZE);
if (!uECC_valid_public_key(pub_key)) {
dtls_warn("invalid public key\n");
}

if (!uECC_shared_secret(pub_key, priv, result)) {
dtls_warn("cannot generate ECDH shared secret\n");
return 0;
}

return key_size;
return dtls_ecdh_pre_master_secret2(priv_key, pub_key, key_size, curve,
result, result_len);
}

void
dtls_ecdsa_generate_key(unsigned char *priv_key,
unsigned char *pub_key_x,
unsigned char *pub_key_y,
size_t key_size) {
const dtls_ecdh_curve curve = default_curve;
uint8_t pub_key[2 * DTLS_EC_KEY_SIZE];

assert(key_size == DTLS_EC_KEY_SIZE);
if (!uECC_make_key(pub_key, priv_key)
|| !uECC_valid_public_key(pub_key)) {
int res = dtls_ecdsa_generate_key2(priv_key, pub_key, key_size, curve);
if (res > 0) {
memcpy(pub_key_x, pub_key, res);
memcpy(pub_key_y, pub_key + res, res);
}
}

int
dtls_ecdsa_generate_key2(unsigned char *priv_key,
unsigned char *pub_key,
size_t key_size,
dtls_ecdh_curve curve) {
uECC_Curve uecc_curve;
if (!get_uecc_curve(curve, &uecc_curve)) {
dtls_warn("curve %" PRIu16 " not supported\n", curve);
return -1;
}

assert(key_size >= (unsigned int)uECC_curve_private_key_size(uecc_curve));
assert(2 * key_size >= (unsigned int)uECC_curve_public_key_size(uecc_curve));

if (!uECC_make_key(pub_key, priv_key, uecc_curve)
|| !uECC_valid_public_key(pub_key, uecc_curve)) {
dtls_crit("cannot generate ECC key pair\n");
return 0;
}
memcpy(pub_key_x, pub_key, key_size);
memcpy(pub_key_y, pub_key + key_size, key_size);
return uECC_curve_private_key_size(uecc_curve);
}

/* rfc4492#section-5.4 */
void
dtls_ecdsa_create_sig_hash(const unsigned char *priv_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
uint32_t point_r[9], uint32_t point_s[9]) {
const dtls_ecdh_curve curve = default_curve;
dtls_ecdsa_create_sig_hash2(priv_key, key_size,
sign_hash, sign_hash_size,
curve, point_r, point_s);

}

int
dtls_ecdsa_create_sig_hash2(const unsigned char *priv_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
dtls_ecdh_curve curve,
uint32_t point_r[9], uint32_t point_s[9]) {
uint8_t sign[2 * DTLS_EC_KEY_SIZE];
uECC_Curve uecc_curve;
int curve_size;
if (!get_uecc_curve(curve, &uecc_curve)) {
dtls_warn("curve %" PRIu16 " not supported\n", curve);
return -1;
}

curve_size = uECC_curve_private_key_size(uecc_curve);

assert(key_size == DTLS_EC_KEY_SIZE);
assert(sign_hash_size >= uECC_BYTES);
assert(sizeof(sign) >= 2 * uECC_BYTES);
uECC_sign(priv_key, sign_hash, sign);
assert(key_size >= (unsigned int)curve_size);
assert(sign_hash_size >= (unsigned int)curve_size);
assert(sizeof(sign) >= 2 * (unsigned int)curve_size);
if (!uECC_sign(priv_key, sign_hash, sign_hash_size, sign, uecc_curve)) {
dtls_warn("cannot create signature\n");
return -1;
}

dtls_ec_key_to_uint32(sign, DTLS_EC_KEY_SIZE, point_r);
dtls_ec_key_to_uint32(sign + DTLS_EC_KEY_SIZE, DTLS_EC_KEY_SIZE, point_s);
dtls_ec_key_to_uint32(sign, curve_size, point_r);
dtls_ec_key_to_uint32(sign + curve_size, curve_size, point_s);
return 2 * curve_size;
}

void
Expand Down Expand Up @@ -517,20 +608,39 @@ dtls_ecdsa_verify_sig_hash(const unsigned char *pub_key_x,
const unsigned char *pub_key_y, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
unsigned char *result_r, unsigned char *result_s) {
const dtls_ecdh_curve curve = default_curve;
uint8_t pub_key[2 * DTLS_EC_KEY_SIZE];
memcpy(pub_key, pub_key_x, key_size);
memcpy(pub_key + key_size, pub_key_y, key_size);
return dtls_ecdsa_verify_sig_hash2(pub_key, key_size,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, the size of pub_key isn't that of the passed in key_size.

sign_hash, sign_hash_size,
curve,
result_r, result_s);
}

int
dtls_ecdsa_verify_sig_hash2(const unsigned char *pub_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
dtls_ecdh_curve curve,
unsigned char *result_r, unsigned char *result_s) {
uint8_t sign[2 * DTLS_EC_KEY_SIZE];
uECC_Curve uecc_curve;
int curve_size;
if (!get_uecc_curve(curve, &uecc_curve)) {
dtls_warn("curve %" PRIu16 " not supported\n", curve);
return -1;
}
(void)result_r;
(void)result_s;

assert(key_size == DTLS_EC_KEY_SIZE);
assert(sign_hash_size >= uECC_BYTES);
assert(sizeof(sign) >= 2 * uECC_BYTES);
curve_size = uECC_curve_public_key_size(uecc_curve);

assert(key_size == (unsigned int)curve_size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That fires on my interop test. Adding

dtls_warn("%d != %d\n", (int) key_size, curve_size);

I get

Feb 26 16:46:45 WARN 32 != 64
tinydtls-server: crypto.c:638: dtls_ecdsa_verify_sig_hash2: Assertion `key_size == (unsigned int)curve_size' failed.

AFAIK, the key-size is passed in as sizeof(config->keyx.ecdsa.other_pub_x) in dtls.c, line 2589

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beside the assert, the first interop-tests with Californium are successful.
Great!

assert(sizeof(sign) >= (unsigned int)curve_size);

/* clear sign to avoid maybe-unitialized warning */
memset(sign, 0, sizeof(sign));
memcpy(pub_key, pub_key_x, key_size);
memcpy(pub_key + key_size, pub_key_y, key_size);
return uECC_verify(pub_key, sign_hash, sign);
return uECC_verify(pub_key, sign_hash, sign_hash_size, sign, uecc_curve);
}

int
Expand Down
78 changes: 75 additions & 3 deletions crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,18 @@ typedef uint8_t dtls_cipher_index_t;
typedef enum { AES128=0
} dtls_crypto_alg;

typedef enum {
DTLS_ECDH_CURVE_SECP256R1
} dtls_ecdh_curve;
/**
* Curve type as specified in the TLS supported elliptic curves
* extension (@see [RFC 8422, Section 5.1.1](https://www.rfc-editor.org/rfc/rfc8422#section-5.1.1).
*
* The only supported value so far is TLS_EXT_ELLIPTIC_CURVES_SECP256R1
*/
typedef uint16_t dtls_ecdh_curve;

/**
* @deprecated {Defined for backwards compatibility.}
*/
#define DTLS_ECDH_CURVE_SECP256R1 TLS_EXT_ELLIPTIC_CURVES_SECP256R1

/** Crypto context for TLS_PSK_WITH_AES_128_CCM_8 cipher suite. */
typedef struct {
Expand Down Expand Up @@ -454,15 +463,70 @@ int dtls_ecdh_pre_master_secret(unsigned char *priv_key,
unsigned char *result,
size_t result_len);

/**
* Generates the pre_master_sercet from given own private key @p
* priv_key and remote public key @p pub_key for the curve @p curve.
* This function returns the generated shared secret in @p result of
* size @p result_len. On success, the return value give the actual
* number of bytes written to @p result. The return @c 0 indicates
* an error.
*
* @param priv_key The own private key. The size of this key is
* defined by the selected @p curve and is passed
* in @p key_size.
* @param pub_key The remote public key. The size of this key is
* defined by the selected @p curve (usually twice
* @p key_size.
* @param key_size Length of @p priv_key in bytes.
* @param curve The elliptic curve to use.
* @param result The derived pre master secret.
* @param result_len The maximum length of the derived pre master secret.
* in @p result.
* @return The actual length of @p result or <= 0 on error.
*/
int dtls_ecdh_pre_master_secret2(const unsigned char *priv_key,
const unsigned char *pub_key,
size_t key_size,
dtls_ecdh_curve curve,
unsigned char *result,
size_t result_len);

void dtls_ecdsa_generate_key(unsigned char *priv_key,
unsigned char *pub_key_x,
unsigned char *pub_key_y,
size_t key_size);

/**
* Generates a key pair for the given curve @p curve and stores the
* private part in @p priv_key and the public part in @p pub_key. The
* storage that must be provided for @p priv_key and @p pub_key is
* determined by @p curve. Usually, @p pub_key requires 2 * @p
* key_size. This function returns the actual number of bytes written
* into @p priv_key on success, or @c 0 otherwise.
*
* @param priv_key Storage for the generated private key.
* @param pub_key Storage for the generated public key.
* @param key_size The amount of storage for @p priv_key.
* @param curve Storage for the generated public key.
* @return The number of bytes written into @p priv_key, or @c 0 on error.
*/
int dtls_ecdsa_generate_key2(unsigned char *priv_key,
unsigned char *pub_key,
size_t key_size,
dtls_ecdh_curve curve);

void dtls_ecdsa_create_sig_hash(const unsigned char *priv_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
uint32_t point_r[9], uint32_t point_s[9]);

/**
* FIXME: document function
*/
int dtls_ecdsa_create_sig_hash2(const unsigned char *priv_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
dtls_ecdh_curve curve,
uint32_t point_r[9], uint32_t point_s[9]);

void dtls_ecdsa_create_sig(const unsigned char *priv_key, size_t key_size,
const unsigned char *client_random, size_t client_random_size,
const unsigned char *server_random, size_t server_random_size,
Expand All @@ -474,6 +538,14 @@ int dtls_ecdsa_verify_sig_hash(const unsigned char *pub_key_x,
const unsigned char *sign_hash, size_t sign_hash_size,
unsigned char *result_r, unsigned char *result_s);

/**
* FIXME: document function
*/
int dtls_ecdsa_verify_sig_hash2(const unsigned char *pub_key, size_t key_size,
const unsigned char *sign_hash, size_t sign_hash_size,
dtls_ecdh_curve curve,
unsigned char *result_r, unsigned char *result_s);

int dtls_ecdsa_verify_sig(const unsigned char *pub_key_x,
const unsigned char *pub_key_y, size_t key_size,
const unsigned char *client_random, size_t client_random_size,
Expand Down
Loading