Skip to content
This repository has been archived by the owner on Mar 5, 2024. It is now read-only.

Commit

Permalink
Significant speedup on EC-DSA verfification: Accelerates ECC point mu…
Browse files Browse the repository at this point in the history
…ltiplication and modular reduction by removing constant-time bottlenecks (not needed in this process as verification only deals with public information). Slightly increased code size (same lines of expected result for other ECC optmizations).

Fix to make ECC Key Generation FIPS 186-4 compliant
- Previous version used a simplified method

Additional check in ecc_valid_public_key
- Also check that the public key is not the generator of P-256

Fix in double_bytes function
- Previous version used by Colin O'Flynn to show power analysis threat

Change in HMAC API:
- API now ensures it erases secrets before exiting

Added Authors file
- Clarifies contacts for responsible disclosure

Signed-off-by: Constanza Heath <[email protected]>
  • Loading branch information
Constanza Heath committed Mar 10, 2017
1 parent d04e95d commit e6cffb8
Show file tree
Hide file tree
Showing 17 changed files with 137 additions and 59 deletions.
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Open Source Maintainer: Constanza Heath <[email protected]>
Author: Rafael Misoczki <[email protected]>
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ include config.mk

all:
$(MAKE) -C lib
ifeq ($(ENABLE_TESTS),true)
$(MAKE) -C tests
endif

clean:
$(MAKE) -C lib clean
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.5
0.2.6
8 changes: 8 additions & 0 deletions config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CC:=gcc
CFLAGS:=-Os -std=c99 -Wall -Wextra -D_ISOC99_SOURCE -MMD -I../lib/include/ -I../lib/source/ -I../tests/include/
vpath %.c ../lib/source/
ENABLE_TESTS=true

# override MinGW built-in recipe
%.o: %.c
Expand All @@ -18,4 +19,11 @@ ifeq ($(OS),Windows_NT)
DOTEXE:=.exe
endif

# DO NOT EDIT THIS:
ifeq ($(ENABLE_TESTS), true)
CFLAGS += -DENABLE_TESTS
else
CFLAGS += -DDISABLE_TESTS
endif

################################################################################
4 changes: 4 additions & 0 deletions documentation/tinycrypt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ Specific Remarks
memory comparison function (such as compare_constant_time
function provided in lib/utils.c).

* The tc_hmac_final function, responsible for computing the message tag,
cleans the state context before exiting. Thus, applications do not need to
clean the TCHmacState_t ctx after calling tc_hmac_final.

* HMAC-PRNG:

* Before using HMAC-PRNG, you *must* find an entropy source to produce a seed.
Expand Down
4 changes: 0 additions & 4 deletions lib/include/tinycrypt/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ extern "C" {
#define NULL ((void *)0)
#endif

#ifndef bool
enum {false, true} bool;
#endif

#define TC_CRYPTO_SUCCESS 1
#define TC_CRYPTO_FAIL 0

Expand Down
41 changes: 39 additions & 2 deletions lib/include/tinycrypt/ecc.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@
extern "C" {
#endif

/* Word size (4 bytes considering 32-bits architectures) */
#define WORD_SIZE 4
/* Number of words of 32 bits to represent an element of the the curve p-256: */
#define NUM_ECC_DIGITS 8
/* Number of bytes to represent an element of the the curve p-256: */
#define NUM_ECC_BYTES (4*NUM_ECC_DIGITS)
#define NUM_ECC_BYTES (WORD_SIZE*NUM_ECC_DIGITS)

/* struct to represent a point of the curve (uses X and Y coordinates): */
typedef struct EccPoint {
Expand Down Expand Up @@ -218,6 +220,8 @@ void vli_modSquare_fast(uint32_t *p_result, uint32_t *p_left);
* @param p_right IN -- buffer p_right in (p_left * p_right) % p_mod.
* @param p_mod IN -- module.
* @param p_barrett IN -- used for Barrett reduction.
* @note Side-channel countermeasure: algorithm strengthened against timing
* attack.
*/
void vli_modMult(uint32_t *p_result, uint32_t *p_left, uint32_t *p_right,
uint32_t *p_mod, uint32_t *p_barrett);
Expand All @@ -229,10 +233,27 @@ void vli_modMult(uint32_t *p_result, uint32_t *p_left, uint32_t *p_right,
* @param p_input IN -- buffer p_input in (1/p_intput) % p_mod.
* @param p_mod IN -- module.
* @param p_barrett IN -- used for Barrett reduction.
* @note Side-channel countermeasure: algorithm strengthened against timing
* attack.
*/
void vli_modInv(uint32_t *p_result, uint32_t *p_input,
uint32_t *p_mod, uint32_t *p_barrett);

/*
* @brief modular reduction based on Barrett's method
* @param p_result OUT -- p_product % p_mod.
* @param p_product IN -- buffer p_product in (p_product % p_mod).
* @param p_mod IN -- buffer p_mod in (p_product % p_mod).
* @param p_barrett -- used for Barrett reduction.
* @note Side-channel countermeasure: algorithm strengthened against timing
* attack.
*/
void vli_mmod_barrett(
uint32_t *p_result,
uint32_t *p_product,
uint32_t *p_mod,
uint32_t *p_barrett);

/*
* @brief Check if a point is zero.
* @return Returns 1 if p_point is the point at infinity, 0 otherwise.
Expand Down Expand Up @@ -271,10 +292,26 @@ void EccPoint_add(EccPointJacobi *P1, EccPointJacobi *P2);
* @param p_result OUT -- Product of p_point by p_scalar.
* @param p_point IN -- Elliptic curve point
* @param p_scalar IN -- Scalar integer
* @note Side-channel countermeasure: algorithm strengthened against timing
* attack.
*/
void EccPoint_mult(EccPointJacobi *p_result, EccPoint *p_point,
void EccPoint_mult_safe(EccPointJacobi *p_result, EccPoint *p_point,
uint32_t *p_scalar);

/*
* @brief Fast elliptic curve scalar multiplication with result in Jacobi
* coordinates
* @note non constant time
* @param p_result OUT -- Product of p_point by p_scalar.
* @param p_point IN -- Elliptic curve point
* @param p_scalar IN -- Scalar integer
* @note algorithm NOT strengthened against timing attack.
*/
void EccPoint_mult_unsafe(
EccPointJacobi *p_result,
EccPoint *p_point,
uint32_t *p_scalar);

/*
* @brief Convert an integer in standard octet representation to native format.
* @return returns TC_CRYPTO_SUCCESS (1)
Expand Down
20 changes: 10 additions & 10 deletions lib/include/tinycrypt/ecc_dh.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ extern "C" {

/**
* @brief Create a public/private key pair.
* @return returns TC_CRYPTO_SUCCESS (1) if the key pair was generated successfully
* @return returns TC_CRYPTO_SUCCESS (1) if key pair was generated successfully
* returns TC_CRYPTO_FAIL (0) if:
* the private key is 0
*
Expand All @@ -90,13 +90,15 @@ extern "C" {
*
* @note You must use a new non-predictable random number to generate each
* new key pair.
* @note p_random must have NUM_ECC_DIGITS*2 bits of entropy to eliminate
* bias in keys.
*
* @note side-channel countermeasure: algorithm strengthened against timing
* attack.
*/
int32_t ecc_make_key(EccPoint *p_publicKey,
uint32_t p_privateKey[NUM_ECC_DIGITS],
uint32_t p_random[NUM_ECC_DIGITS]);
uint32_t p_random[2 * NUM_ECC_DIGITS]);

/**
* @brief Determine whether or not a given point is on the chosen elliptic curve
Expand All @@ -106,15 +108,16 @@ int32_t ecc_make_key(EccPoint *p_publicKey,
* returns -2 if: curve_p - p_publicKey->x != 1 or
* curve_p - p_publicKey->y != 1
* returns -3 if: y^2 != x^3 + ax + b
* returns -4 if: public key is the group generator
*
* @param p_publicKey IN -- The point to be checked.
*/
int32_t ecc_valid_public_key(EccPoint *p_publicKey);

/**
* @brief Compute a shared secret given your secret key and someone else's
* public key.
* @return returns TC_CRYPTO_SUCCESS (1) if the shared secret was computed successfully
* @return returns TC_CRYPTO_SUCCESS (1) if the secret was computed successfully
* returns TC_CRYPTO_FAIL (0) otherwise
*
* @param p_secret OUT -- The shared secret value.
Expand All @@ -125,12 +128,9 @@ int32_t ecc_valid_public_key(EccPoint *p_publicKey);
* attacks. The random multiplier should probably be different for each
* invocation of ecdh_shared_secret().
*
* @note It is recommended that you hash the result of ecdh_shared_secret before
* using it for symmetric encryption or HMAC. If you do not hash the shared
* secret, you must call ecc_valid_public_key() to verify that the remote side's
* public key is valid. If this is not done, an attacker could create a public
* key that would cause your use of the shared secret to leak information about
* the private key.
* @warning It is recommended to use the output of ecdh_shared_secret() as the
* input of a recommended Key Derivation Function (see NIST SP 800-108) in
* order to produce a symmetric key.
*/
int32_t ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS], EccPoint *p_publicKey,
uint32_t p_privateKey[NUM_ECC_DIGITS]);
Expand Down
1 change: 1 addition & 0 deletions lib/include/tinycrypt/hmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ int32_t tc_hmac_update(TCHmacState_t ctx,
* ctx == NULL or
* key == NULL or
* taglen != TC_SHA256_DIGEST_SIZE
* @note 'ctx' is erased before exiting (this must never be changed/removed).
* @note Assumes the tag bufer is at least sizeof(hmac_tag_size(state)) bytes
* state has been initialized by tc_hmac_init
* @param tag IN/OUT -- buffer to receive computed HMAC tag
Expand Down
27 changes: 24 additions & 3 deletions lib/source/ecc.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ static void vli_clear(uint32_t *p_vli)
}
}

/* Returns nonzero if bit p_bit of p_vli is set. */
/* Returns nonzero if bit p_bit of p_vli is set.
* It is assumed that the value provided in 'bit' is within
* the boundaries of the word-array 'p_vli'.*/
static uint32_t vli_testBit(uint32_t *p_vli, uint32_t p_bit)
{
return (p_vli[p_bit / 32] & (1 << (p_bit % 32)));
Expand Down Expand Up @@ -235,7 +237,7 @@ static void vli_square(uint32_t *p_result, uint32_t *p_left)
}

/* Computes p_result = p_product % curve_p using Barrett reduction. */
static void vli_mmod_barrett(uint32_t *p_result, uint32_t *p_product,
void vli_mmod_barrett(uint32_t *p_result, uint32_t *p_product,
uint32_t *p_mod, uint32_t *p_barrett)
{
uint32_t i;
Expand Down Expand Up @@ -547,7 +549,7 @@ void EccPoint_add(EccPointJacobi *P1, EccPointJacobi *P2)
*
* p_result = p_scalar * p_point.
*/
void EccPoint_mult(EccPointJacobi *p_result, EccPoint *p_point, uint32_t *p_scalar)
void EccPoint_mult_safe(EccPointJacobi *p_result, EccPoint *p_point, uint32_t *p_scalar)
{

int32_t i;
Expand All @@ -568,6 +570,25 @@ void EccPoint_mult(EccPointJacobi *p_result, EccPoint *p_point, uint32_t *p_scal
}
}

/* Ellptic curve scalar multiplication with result in Jacobi coordinates */
/* p_result = p_scalar * p_point */
void EccPoint_mult_unsafe(EccPointJacobi *p_result, EccPoint *p_point, uint32_t *p_scalar)
{
int i;
EccPointJacobi p_point_jacobi;
EccPoint_fromAffine(p_result, p_point);
EccPoint_fromAffine(&p_point_jacobi, p_point);

for(i = vli_numBits(p_scalar) - 2; i >= 0; i--)
{
EccPoint_double(p_result);
if (vli_testBit(p_scalar, i))
{
EccPoint_add(p_result, &p_point_jacobi);
}
}
}

/* -------- Conversions between big endian and little endian: -------- */

void ecc_bytes2native(uint32_t p_native[NUM_ECC_DIGITS],
Expand Down
19 changes: 14 additions & 5 deletions lib/source/ecc_dh.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,36 @@
extern uint32_t curve_p[NUM_ECC_DIGITS];
extern uint32_t curve_b[NUM_ECC_DIGITS];
extern uint32_t curve_n[NUM_ECC_DIGITS];
extern uint32_t curve_pb[NUM_ECC_DIGITS + 1];
extern EccPoint curve_G;

int32_t ecc_make_key(EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS],
uint32_t p_random[NUM_ECC_DIGITS])
uint32_t p_random[NUM_ECC_DIGITS * 2])
{
// computing modular reduction of p_random (see FIPS 186.4 B.4.1):
vli_mmod_barrett(p_privateKey, p_random, curve_p, curve_pb);

/* Make sure the private key is in the range [1, n-1].
* For the supported curve, n is always large enough
* that we only need to subtract once at most.
*/
uint32_t p_tmp[NUM_ECC_DIGITS];

vli_set(p_privateKey, p_random);
vli_sub(p_tmp, p_privateKey, curve_n, NUM_ECC_DIGITS);

vli_cond_set(p_privateKey, p_privateKey, p_tmp,
vli_cmp(curve_n, p_privateKey, NUM_ECC_DIGITS) == 1);

/* erasing temporary buffer used to store secret: */
for (uint32_t i = 0; i < NUM_ECC_DIGITS; i++)
p_tmp[i] = 0;

if (vli_isZero(p_privateKey)) {
return TC_CRYPTO_FAIL; /* The private key cannot be 0 (mod p). */
}

EccPointJacobi P;

EccPoint_mult(&P, &curve_G, p_privateKey);
EccPoint_mult_safe(&P, &curve_G, p_privateKey);
EccPoint_toAffine(p_publicKey, &P);

return TC_CRYPTO_SUCCESS;
Expand Down Expand Up @@ -102,6 +107,10 @@ int32_t ecc_valid_public_key(EccPoint *p_publicKey)
return -3;
}

if (vli_cmp(p_publicKey->x, curve_G.x, NUM_ECC_DIGITS) == 0 &&
vli_cmp(p_publicKey->y, curve_G.y, NUM_ECC_DIGITS) == 0 )
return -4;

return 0;
}

Expand All @@ -112,7 +121,7 @@ int32_t ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS],
EccPoint p_point;
EccPointJacobi P;

EccPoint_mult(&P, p_publicKey, p_privateKey);
EccPoint_mult_safe(&P, p_publicKey, p_privateKey);
if (EccPointJacobi_isZero(&P)) {
return TC_CRYPTO_FAIL;
}
Expand Down
14 changes: 6 additions & 8 deletions lib/source/ecc_dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ int32_t ecdsa_sign(uint32_t r[NUM_ECC_DIGITS], uint32_t s[NUM_ECC_DIGITS],
vli_cond_set(k, k, tmp, vli_cmp(curve_n, k, NUM_ECC_DIGITS) == 1);

/* tmp = k * G */
EccPoint_mult(&P, &curve_G, k);
EccPoint_mult_safe(&P, &curve_G, k);
EccPoint_toAffine(&p_point, &P);

/* r = x1 (mod n) */
Expand Down Expand Up @@ -101,17 +101,15 @@ int32_t ecdsa_verify(EccPoint *p_publicKey, uint32_t p_hash[NUM_ECC_DIGITS],
vli_modMult(u2, r, z, curve_n, curve_nb); /* u2 = r/s */

/* calculate P = u1*G + u2*Q */
EccPoint_mult(&P, &curve_G, u1);
EccPoint_mult(&R, p_publicKey, u2);
EccPoint_mult_unsafe(&P, &curve_G, u1);
EccPoint_mult_unsafe(&R, p_publicKey, u2);
EccPoint_add(&P, &R);
EccPoint_toAffine(&p_point, &P);

/* Accept only if P.x == r. */
vli_cond_set(
p_point.x,
p_point.x,
z,
vli_sub(z, p_point.x, curve_n, NUM_ECC_DIGITS));
if (!vli_sub(z, p_point.x, curve_n, NUM_ECC_DIGITS)) {
vli_set(p_point.x, z);
}

return (vli_cmp(p_point.x, r, NUM_ECC_DIGITS) == 0);
}
7 changes: 2 additions & 5 deletions lib/source/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,11 @@ void _set(void *to, uint8_t val, uint32_t len)
}

/*
* Doubles the value of a byte for values up to 127. Original 'return
* ((a<<1) ^ ((a>>7) * 0x1b))' re-written to avoid extra multiplication which
* the compiler won't be able to optimize
* Doubles the value of a byte for values up to 127.
*/
uint8_t _double_byte(uint8_t a)
{
return (a & MASK_MOST_SIG_BIT) ?
((a << 1) ^ MASK_TWENTY_SEVEN) : (a << 1);
return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN));
}

int32_t _compare(const uint8_t *a, const uint8_t *b, size_t size)
Expand Down
Loading

0 comments on commit e6cffb8

Please sign in to comment.