Changed point multiplication algorithm to be resistant to side-channel attacks.

This commit is contained in:
Ken MacKay
2013-06-10 22:26:36 -07:00
parent 0b74c9c565
commit ca9b365f24
5 changed files with 262 additions and 433 deletions
+43 -183
View File
@@ -6,19 +6,17 @@ A small ECDH and ECDSA implementation for 32-bit microcontrollers.
Features
--------
* BSD 2-clause license.
* Resistant to known side-channel attacks.
* Written in C, with optional inline assembly for ARM and Thumb platforms.
* Small code size: as low as 1800 bytes when compiled for Thumb (eg, Cortex-M0).
* Small code size: ECDH in as little as 2KB, ECDH + ECDSA in as little as 3KB when compiled for Thumb (eg, Cortex-M0).
* No dynamic memory allocation.
* Reasonably fast: on an LPC1114 at 48MHz (ARM Cortex-M0, 32-cycle 32x32 bit multiply), 192-bit ECDH shared secret calculation takes as little as ~150ms (depending on selected optimizations).
* Reasonably fast: on an LPC1114 at 48MHz (ARM Cortex-M0, 32-cycle 32x32 bit multiply), 192-bit ECDH shared secret calculation takes as little as ~175ms (depending on selected optimizations).
* Support for 4 standard curves: secp128r1, secp192r1, secp256r1, and secp384r1
* Optional optimizations so you can choose between code size and speed.
* BSD 2-clause license.
Usage Notes
-----------
**NOTE:** This implementation is not designed to be resistant to timing attacks or other side-channel attacks. To thwart timing attacks, you can use a timer to ensure that all ECDH operations take the same amount of time (ie, the longest time it would ever take). You can determine the maximum time for ECDH by using a private key that is all 0xffffffff.
#### Integer Representation ####
To reduce code size, all large integers are represented using little-endian words - so the least significant word is first. For example, the standard representation of the prime modulus for the curve secp128r1 is `FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF`; in micro-ecc, this would be represented as `uint32_t p[4] = {0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffd};`.
@@ -38,7 +36,6 @@ Speed and Size
Available optimizations are:
* `ECC_SQUARE_FUNC` - Use a separate function for squaring.
* `ECC_USE_NAF` - Use a non-adjacent form for scalar representation when doing point multiplication.
* `ECC_ASM` - Choose the type of inline assembly to use. The available options are `ecc_asm_none`, `ecc_asm_thumb`, `ecc_asm_thumb2`, and `ecc_asm_arm`.
All tests were performed on an LPC1114 running at 48MHz. The listed code sizes include all code and data required by the micro-ecc library (including `aebi_lmul` when not using assembly),
@@ -50,9 +47,7 @@ The following compiler options were used (using gcc 4.8):
### Effect of optimization settings ###
These tests were performed using the curve secp192r1. Only ECDH code was used (no ECDSA).
#### ECC_ASM defined to ecc_asm_none ####
These tests were performed using the curve secp192r1. Only ECDH code was used (no ECDSA). When enabled, ECC_ASM was defined to `ecc_asm_thumb`.
<table>
<tr>
@@ -62,61 +57,29 @@ These tests were performed using the curve secp192r1. Only ECDH code was used (n
</tr>
<tr>
<td>none</td>
<td>431.9</td>
<td>1948</td>
<td>438.9</td>
<td>2212</td>
</tr>
<tr>
<td>ECC_SQUARE_FUNC</td>
<td>393.0</td>
<td>2152</td>
</tr>
<tr>
<td>ECC_USE_NAF</td>
<td>369.5</td>
<td>2208</td>
</tr>
<tr>
<td>both</td>
<td>337.2</td>
<td>406.7</td>
<td>2412</td>
</tr>
</table>
#### ECC_ASM defined to ecc_asm_thumb ####
<table>
<tr>
<th>Optimizations</th>
<th>ECDH time (ms)</th>
<th>Code size (bytes)</th>
</tr>
<tr>
<td>none</td>
<td>183.9</td>
<td>1772</td>
</tr>
<tr>
<td>ECC_SQUARE_FUNC</td>
<td>172.6</td>
<td>1920</td>
</tr>
<tr>
<td>ECC_USE_NAF</td>
<td>158.2</td>
<td>2032</td>
<td>ECC_ASM</td>
<td>186.3</td>
<td>2036</td>
</tr>
<tr>
<td>both</td>
<td>147.7</td>
<td>2180</td>
<td>175.7</td>
<td>2170</td>
</tr>
</table>
### ECDH for different curves ###
In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` in all cases.
#### No other optimizations (smallest code size) ####
In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` and `ECC_SQUARE_FUNC` was defined to `1` in all cases.
<table>
<tr>
@@ -128,51 +91,23 @@ In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` in all cases.
</tr>
<tr>
<td><em>ECDH time (ms):</em></td>
<td>85.5</td>
<td>183.9</td>
<td>473.6</td>
<td>1405.8</td>
<td>89.9</td>
<td>175.7</td>
<td>465.1</td>
<td>1370.3</td>
</tr>
<tr>
<td><em>Code size (bytes):</em></td>
<td>1908</td>
<td>1772</td>
<td>2104</td>
<td>1832</td>
</tr>
</table>
#### All optimizations (fastest) ####
<table>
<tr>
<th></th>
<th>secp128r1</th>
<th>secp192r1</th>
<th>secp256r1</th>
<th>secp384r1</th>
</tr>
<tr>
<td><em>ECDH time (ms):</em></td>
<td>75.9</td>
<td>147.7</td>
<td>385.8</td>
<td>1128.2</td>
</tr>
<tr>
<td><em>Code size (bytes):</em></td>
<td>2332</td>
<td>2180</td>
<td>2516</td>
<td>2324</td>
<td>2170</td>
<td>2512</td>
<td>2244</td>
</tr>
</table>
### ECDSA speed and combined code size ###
In these tests, the measured speed is the time to verify an ECDSA signature. The measured code size is the combined code size for ECDH and ECDSA. `ECC_ASM` was defined to `ecc_asm_thumb` in all cases.
#### No other optimizations (smallest code size) ####
In these tests, the measured speed is the time to verify an ECDSA signature. The measured code size is the combined code size for ECDH and ECDSA. `ECC_ASM` was defined to `ecc_asm_thumb` and `ECC_SQUARE_FUNC` was defined to `1` in all cases.
<table>
<tr>
@@ -184,51 +119,23 @@ In these tests, the measured speed is the time to verify an ECDSA signature. The
</tr>
<tr>
<td><em>ECDSA verify time (ms):</em></td>
<td>108.6</td>
<td>230.6</td>
<td>590.2</td>
<td>1729.7</td>
<td>106.7</td>
<td>217.1</td>
<td>555.2</td>
<td>1576.1</td>
</tr>
<tr>
<td><em>Code size (bytes):</em></td>
<td>2638</td>
<td>2526</td>
<td>2846</td>
<td>2674</td>
</tr>
</table>
#### All optimizations (fastest) ####
<table>
<tr>
<th></th>
<th>secp128r1</th>
<th>secp192r1</th>
<th>secp256r1</th>
<th>secp384r1</th>
</tr>
<tr>
<td><em>ECDSA verify time (ms):</em></td>
<td>108.0</td>
<td>219.0</td>
<td>558.5</td>
<td>1615.0</td>
</tr>
<tr>
<td><em>Code size (bytes):</em></td>
<td>3046</td>
<td>2918</td>
<td>3248</td>
<td>3062</td>
<td>3138</td>
<td>3014</td>
<td>3334</td>
<td>3158</td>
</tr>
</table>
### Maximum stack usage ###
In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` in all cases. The table values are the maximum possible stack usage for each function, in bytes.
#### No other optimizations (smallest code size) ####
In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` and `ECC_SQUARE_FUNC` was defined to `1` in all cases. The table values are the maximum possible stack usage for each function, in bytes.
<table>
<tr>
@@ -240,57 +147,10 @@ In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` in all cases. The table
</tr>
<tr>
<td><em>ecc_make_key()</em></td>
<td>392</td>
<td>488</td>
<td>624</td>
<td>848</td>
</tr>
<tr>
<td><em>ecc_valid_public_key()</em></td>
<td>184</td>
<td>224</td>
<td>304</td>
<td>416</td>
</tr>
<tr>
<td><em>ecdh_shared_secret()</em></td>
<td>336</td>
<td>416</td>
<td>528</td>
<td>680</td>
<td>936</td>
</tr>
<tr>
<td><em>ecdsa_sign()</em></td>
<td>448</td>
<td>568</td>
<td>728</td>
<td>1000</td>
</tr>
<tr>
<td><em>ecdsa_verify()</em></td>
<td>480</td>
<td>616</td>
<td>792</td>
<td>1096</td>
</tr>
</table>
#### All optimizations (fastest) ####
<table>
<tr>
<th></th>
<th>secp128r1</th>
<th>secp192r1</th>
<th>secp256r1</th>
<th>secp384r1</th>
</tr>
<tr>
<td><em>ecc_make_key()</em></td>
<td>472</td>
<td>600</td>
<td>760</td>
<td>1056</td>
</tr>
<tr>
<td><em>ecc_valid_public_key()</em></td>
@@ -301,23 +161,23 @@ In these tests, `ECC_ASM` was defined to `ecc_asm_thumb` in all cases. The table
</tr>
<tr>
<td><em>ecdh_shared_secret()</em></td>
<td>496</td>
<td>640</td>
<td>360</td>
<td>456</td>
<td>584</td>
<td>816</td>
<td>1144</td>
</tr>
<tr>
<td><em>ecdsa_sign()</em></td>
<td>528</td>
<td>680</td>
<td>864</td>
<td>1208</td>
<td>400</td>
<td>504</td>
<td>640</td>
<td>888</td>
</tr>
<tr>
<td><em>ecdsa_verify()</em></td>
<td>488</td>
<td>624</td>
<td>792</td>
<td>1104</td>
<td>400</td>
<td>520</td>
<td>672</td>
<td>952</td>
</tr>
</table>
+203 -241
View File
@@ -1044,251 +1044,202 @@ static void vli_modInv(uint32_t *p_result, uint32_t *p_input, uint32_t *p_mod)
/* ------ Point operations ------ */
/* Clears a point (set it to the point at infinity). */
static void EccPoint_clear(EccPoint *p_point)
{
vli_clear(p_point->x);
vli_clear(p_point->y);
}
/* Copies a point. */
static void EccPoint_copy(EccPoint *p_dest, EccPoint *p_src)
{
if(p_dest != p_src)
{
vli_set(p_dest->x, p_src->x);
vli_set(p_dest->y, p_src->y);
}
}
/* Returns 1 if p_point is the point at infinity, 0 otherwise. */
static int EccPoint_isZero(EccPoint *p_point)
{
return (vli_isZero(p_point->x) && vli_isZero(p_point->y));
}
/* Modified Jacobian point doubling. Note that we use the fact that a is equivalent to -3 (mod p) for the supported
curves, we can transform M = (3 * x1^2 + a * Z1^2) to M = 3 * (x1 + Z1^2) * (x1 - Z1^2)
/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates.
From http://eprint.iacr.org/2011/338.pdf
*/
static void EccPoint_double_projective(EccPoint *P3, uint32_t *Z3, EccPoint *P1, uint32_t *Z1)
/* Double in place */
static void EccPoint_double_jacobian(uint32_t *X1, uint32_t *Y1, uint32_t *Z1)
{
uint32_t l_tmp1[NUM_ECC_DIGITS];
uint32_t l_tmp2[NUM_ECC_DIGITS];
uint32_t l_tmp3[NUM_ECC_DIGITS];
uint32_t l_tmp4[NUM_ECC_DIGITS];
/* t1 = X, t2 = Y, t3 = Z */
uint32_t t4[NUM_ECC_DIGITS];
uint32_t t5[NUM_ECC_DIGITS];
uint32_t t9[NUM_ECC_DIGITS];
if(vli_isZero(Z1))
{
vli_clear(Z3);
return;
}
vli_modSquare_fast(l_tmp1, Z1); /* tmp1 = Z1^2 */
vli_modAdd(l_tmp2, P1->x, l_tmp1, curve_p); /* tmp2 = x1 + Z1^2 */
vli_modSub(l_tmp1, P1->x, l_tmp1, curve_p); /* tmp1 = x1 - Z1^2 */
vli_modMult_fast(l_tmp1, l_tmp1, l_tmp2); /* tmp1 = (x1 + Z1^2) * (x1 - Z1^2) */
vli_modAdd(l_tmp2, l_tmp1, l_tmp1, curve_p); /* tmp2 = 2 * (x1 + Z1^2) * (x1 - Z1^2) */
vli_modAdd(l_tmp1, l_tmp2, l_tmp1, curve_p); /* tmp1 = M = 3 * (x1 + Z1^2) * (x1 - Z1^2) */
vli_modMult_fast(Z3, P1->y, Z1); /* Z3 = y1 * Z1 */
vli_modSquare_fast(t4, Y1); /* t4 = y1^2 */
vli_modMult_fast(t5, X1, t4); /* t5 = x1*y1^2 = A */
vli_modSquare_fast(t4, t4); /* t4 = y1^4 */
vli_modMult_fast(Y1, Y1, Z1); /* t2 = y1*z1 = z3 */
vli_modSquare_fast(Z1, Z1); /* t3 = z1^2 */
vli_modAdd(Z3, Z3, Z3, curve_p); /* Z3 = 2 * y1 * Z1 */
vli_modAdd(X1, X1, Z1, curve_p); /* t1 = x1 + z1^2 */
vli_modAdd(Z1, Z1, Z1, curve_p); /* t3 = 2*z1^2 */
vli_modSub(Z1, X1, Z1, curve_p); /* t3 = x1 - z1^2 */
vli_modMult_fast(X1, X1, Z1); /* t1 = x1^2 - z1^4 */
vli_modSquare_fast(l_tmp3, P1->y); /* tmp3 = y1^2 */
vli_modMult_fast(l_tmp2, l_tmp3, P1->x); /* tmp2 = x1 * y1^2 */
vli_modAdd(l_tmp2, l_tmp2, l_tmp2, curve_p); /* tmp2 = 2 * x1 * y1^2 */
vli_modAdd(l_tmp2, l_tmp2, l_tmp2, curve_p); /* tmp2 = S = 4 * x1 * y1^2 */
/* Now tmp1 = M, tmp2 = S */
vli_modAdd(Z1, X1, X1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
vli_modAdd(X1, X1, Z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
if(vli_testBit(X1, 0))
{
uint32_t l_carry = vli_add(X1, X1, curve_p);
vli_rshift1(X1);
X1[NUM_ECC_DIGITS-1] |= l_carry << 31;
}
else
{
vli_rshift1(X1);
}
/* t1 = 3/2*(x1^2 - z1^4) = B */
vli_modAdd(l_tmp4, l_tmp2, l_tmp2, curve_p); /* tmp4 = 2*S */
vli_modSquare_fast(P3->x, l_tmp1); /* x3 = M^2 */
vli_modSub(P3->x, P3->x, l_tmp4, curve_p); /* x3 = T = M^2 - 2*S */
/* Now tmp1 = M, tmp2 = S, x3 = T */
vli_modSquare_fast(Z1, X1); /* t3 = B^2 */
vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - A */
vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
vli_modSub(t5, t5, Z1, curve_p); /* t5 = A - x3 */
vli_modMult_fast(X1, X1, t5); /* t1 = B * (A - x3) */
vli_modSub(t4, X1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
/* tmp3 is still y1^2 at this point */
vli_modSquare_fast(l_tmp3, l_tmp3); /* tmp3 = y1^4 */
vli_modAdd(l_tmp3, l_tmp3, l_tmp3, curve_p); /* tmp3 = 2 * y1^4 */
vli_modAdd(l_tmp3, l_tmp3, l_tmp3, curve_p); /* tmp3 = 4 * y1^4 */
vli_modAdd(l_tmp3, l_tmp3, l_tmp3, curve_p); /* tmp3 = U = 8 * y1^4 */
/* Now tmp1 = M, tmp2 = S, x3 = T, tmp3 = U */
vli_modSub(l_tmp2, l_tmp2, P3->x, curve_p); /* tmp2 = S - T */
vli_modMult_fast(l_tmp2, l_tmp1, l_tmp2); /* tmp2 = M * (S - T) */
vli_modSub(P3->y, l_tmp2, l_tmp3, curve_p); /* y3 = M * (S - T) - U */
vli_set(X1, Z1);
vli_set(Z1, Y1);
vli_set(Y1, t4);
}
/* Modified Jacobian point addition; P3 = P1 + P2. Note that P1 and P3 are in projective coordinates,
and P2 is in affine coordinates. */
static void EccPoint_add_mixed(EccPoint *P3, uint32_t *Z3, EccPoint *P1, uint32_t *Z1, EccPoint *P2)
/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
static void apply_z(uint32_t *X1, uint32_t *Y1, uint32_t *Z)
{
uint32_t l_tmp1[NUM_ECC_DIGITS];
uint32_t l_tmp2[NUM_ECC_DIGITS];
uint32_t l_tmp3[NUM_ECC_DIGITS];
uint32_t l_tmp4[NUM_ECC_DIGITS];
if(EccPoint_isZero(P2))
{
EccPoint_copy(P3, P1);
vli_set(Z3, Z1);
return;
}
if(vli_isZero(Z1))
{
EccPoint_copy(P3, P2);
vli_clear(Z3);
Z3[0] = 1;
return;
}
vli_modSquare_fast(l_tmp1, Z1); /* tmp1 = Z1^2 */
vli_modMult_fast(l_tmp2, l_tmp1, Z1); /* tmp2 = Z1^3 */
vli_modMult_fast(l_tmp1, l_tmp1, P2->x); /* tmp1 = Z1^2 * x2 */
vli_modSub(l_tmp3, l_tmp1, P1->x, curve_p); /* tmp3 = (Z1^2 * x2) - x1 */
/* tmp2 = Z1^3, tmp3 = H */
vli_modMult_fast(l_tmp2, l_tmp2, P2->y); /* tmp2 = Z1^3 * y2 */
vli_modSub(l_tmp4, l_tmp2, P1->y, curve_p); /* tmp4 = (Z1^3 * y2) - y1 */
/* tmp4 = r */
if(vli_isZero(l_tmp3))
{
if(vli_isZero(l_tmp4))
{ /* The points are equal, so we use the doubling formula. */
EccPoint_double_projective(P3, Z3, P1, Z1);
}
else
{
vli_clear(Z3);
}
return;
}
vli_modMult_fast(Z3, Z1, l_tmp3); /* Z3 = H * Z1 */
vli_modSquare_fast(l_tmp1, l_tmp3); /* tmp1 = H^2 */
vli_modMult_fast(l_tmp2, l_tmp1, l_tmp3); /* tmp2 = H^3 */
vli_modMult_fast(l_tmp1, l_tmp1, P1->x); /* tmp1 = H^2 * x1 */
/* tmp1 = H^2 * x1, tmp2 = H^3, tmp3 = H, tmp4 = r */
vli_modAdd(l_tmp3, l_tmp1, l_tmp1, curve_p); /* tmp3 = 2 * H^2 * x1 */
vli_modSquare_fast(P3->x, l_tmp4); /* x3 = r^2 */
vli_modSub(P3->x, P3->x, l_tmp3, curve_p); /* x3 = r^2 - (2 * H^2 * x1) */
vli_modSub(P3->x, P3->x, l_tmp2, curve_p); /* x3 = r^2 - (2 * H^2 * x1) - H^3 */
/* tmp1 = H^2 * x1, tmp2 = H^3, tmp4 = r */
vli_modSub(l_tmp3, l_tmp1, P3->x, curve_p); /* tmp3 = (H^2 * x1) - x3 */
vli_modMult_fast(l_tmp3, l_tmp3, l_tmp4); /* tmp3 = r * ((H^2 * x1) - x3) */
vli_modMult_fast(l_tmp1, l_tmp2, P1->y); /* tmp1 = H^3 * y1 */
vli_modSub(P3->y, l_tmp3, l_tmp1, curve_p); /* y3 = r * ((H^2 * x1) - x3) - (H^3 * y1) */
uint32_t t1[NUM_ECC_DIGITS];
vli_modSquare_fast(t1, Z); /* z^2 */
vli_modMult_fast(X1, X1, t1); /* x1 * z^2 */
vli_modMult_fast(t1, t1, Z); /* z^3 */
vli_modMult_fast(Y1, Y1, t1); /* y1 * z^3 */
}
#if ECC_USE_NAF
/* Computes p_result = p_point * p_scalar. p_result must not be the same as p_point.
Uses modified Jacobian coordinates to reduce divisions, and NAF to reduce point adds. */
static void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint32_t *p_scalar)
/* P = (x1, y1) => 2P, (x2, y2) => P' */
static void XYcZ_initial_double(uint32_t *X1, uint32_t *Y1, uint32_t *X2, uint32_t *Y2, uint32_t *p_initialZ)
{
uint32_t l_tmp[NUM_ECC_DIGITS];
uint32_t Z1[NUM_ECC_DIGITS];
uint32_t l_plus[NUM_ECC_DIGITS];
uint32_t l_minus[NUM_ECC_DIGITS];
uint32_t z[NUM_ECC_DIGITS];
int l_numBits = vli_numBits(p_scalar);
vli_set(X2, X1);
vli_set(Y2, Y1);
uint l_carry;
int i;
vli_clear(Z1);
vli_clear(l_plus);
vli_clear(l_minus);
EccPoint l_neg;
vli_set(l_neg.x, p_point->x);
vli_sub(l_neg.y, curve_p, p_point->y);
l_carry = 0;
for(i = 0; i < l_numBits; ++i)
vli_clear(z);
z[0] = 1;
if(p_initialZ)
{
int l_set = vli_testBit(p_scalar, i);
if((l_carry && !l_set) || (l_set && !l_carry))
{
l_carry = 0;
if(i < l_numBits - 1 && vli_testBit(p_scalar, i + 1))
{
l_minus[i/32] |= (1 << (i%32));
l_carry = 1;
}
else
{
l_plus[i/32] |= (1 << (i%32));
}
}
vli_set(z, p_initialZ);
}
apply_z(X1, Y1, z);
EccPoint_clear(p_result);
EccPoint_double_jacobian(X1, Y1, z);
if(l_carry)
{
EccPoint_add_mixed(p_result, Z1, p_result, Z1, p_point);
}
for(i = l_numBits - 1; i >= 0; --i)
{
unsigned l_mask;
EccPoint_double_projective(p_result, Z1, p_result, Z1);
l_mask = (1 << (i%32));
if(l_plus[i/32] & l_mask)
{
EccPoint_add_mixed(p_result, Z1, p_result, Z1, p_point);
}
else if(l_minus[i/32] & l_mask)
{
EccPoint_add_mixed(p_result, Z1, p_result, Z1, &l_neg);
}
}
vli_modInv(Z1, Z1, curve_p); /* Z1 = 1/Z */
vli_modSquare_fast(l_tmp, Z1); /* tmp = 1/Z^2 */
vli_modMult_fast(p_result->x, p_result->x, l_tmp); /* x = x/Z^2 */
vli_modMult_fast(l_tmp, Z1, l_tmp); /* tmp = 1/Z^3 */
vli_modMult_fast(p_result->y, p_result->y, l_tmp); /* y = y/Z^3 */
apply_z(X2, Y2, z);
}
#else /* ECC_USE_NAF */
/* Computes p_result = p_point * p_scalar. p_result must not be the same as p_point.
Uses modified Jacobian coordinates to reduce divisions. */
static void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint32_t *p_scalar)
/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
or P => P', Q => P + Q
*/
static void XYcZ_add(uint32_t *X1, uint32_t *Y1, uint32_t *X2, uint32_t *Y2)
{
uint32_t l_tmp[NUM_ECC_DIGITS];
uint32_t Z1[NUM_ECC_DIGITS];
/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
uint32_t t5[NUM_ECC_DIGITS];
uint l_numBits = vli_numBits(p_scalar);
int i;
vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
vli_modSquare_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
vli_modMult_fast(X1, X1, t5); /* t1 = x1*A = B */
vli_modMult_fast(X2, X2, t5); /* t3 = x2*A = C */
vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y2 - y1 */
vli_modSquare_fast(t5, Y2); /* t5 = (y2 - y1)^2 = D */
vli_clear(Z1);
EccPoint_clear(p_result);
for(i = l_numBits - 1; i >= 0; --i)
{
EccPoint_double_projective(p_result, Z1, p_result, Z1);
if(vli_testBit(p_scalar, i))
{
EccPoint_add_mixed(p_result, Z1, p_result, Z1, p_point);
}
}
vli_modInv(Z1, Z1, curve_p); /* Z1 = 1/Z */
vli_modSquare_fast(l_tmp, Z1); /* tmp = 1/Z^2 */
vli_modMult_fast(p_result->x, p_result->x, l_tmp); /* x = x/Z^2 */
vli_modSub(t5, t5, X1, curve_p); /* t5 = D - B */
vli_modSub(t5, t5, X2, curve_p); /* t5 = D - B - C = x3 */
vli_modSub(X2, X2, X1, curve_p); /* t3 = C - B */
vli_modMult_fast(Y1, Y1, X2); /* t2 = y1*(C - B) */
vli_modSub(X2, X1, t5, curve_p); /* t3 = B - x3 */
vli_modMult_fast(Y2, Y2, X2); /* t4 = (y2 - y1)*(B - x3) */
vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
vli_modMult_fast(l_tmp, Z1, l_tmp); /* tmp = 1/Z^3 */
vli_modMult_fast(p_result->y, p_result->y, l_tmp); /* y = y/Z^3 */
vli_set(X2, t5);
}
#endif /* ECC_USE_NAF */
/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
or P => P - Q, Q => P + Q
*/
static void XYcZ_addC(uint32_t *X1, uint32_t *Y1, uint32_t *X2, uint32_t *Y2)
{
/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
uint32_t t5[NUM_ECC_DIGITS];
uint32_t t6[NUM_ECC_DIGITS];
uint32_t t7[NUM_ECC_DIGITS];
vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
vli_modSquare_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
vli_modMult_fast(X1, X1, t5); /* t1 = x1*A = B */
vli_modMult_fast(X2, X2, t5); /* t3 = x2*A = C */
vli_modAdd(t5, Y2, Y1, curve_p); /* t4 = y2 + y1 */
vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y2 - y1 */
vli_modSub(t6, X2, X1, curve_p); /* t6 = C - B */
vli_modMult_fast(Y1, Y1, t6); /* t2 = y1 * (C - B) */
vli_modAdd(t6, X1, X2, curve_p); /* t6 = B + C */
vli_modSquare_fast(X2, Y2); /* t3 = (y2 - y1)^2 */
vli_modSub(X2, X2, t6, curve_p); /* t3 = x3 */
vli_modSub(t7, X1, X2, curve_p); /* t7 = B - x3 */
vli_modMult_fast(Y2, Y2, t7); /* t4 = (y2 - y1)*(B - x3) */
vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
vli_modSquare_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */
vli_modSub(t7, t7, t6, curve_p); /* t7 = x3' */
vli_modSub(t6, t7, X1, curve_p); /* t6 = x3' - B */
vli_modMult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */
vli_modSub(Y1, t6, Y1, curve_p); /* t2 = y3' */
vli_set(X1, t7);
}
static void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint32_t *p_scalar, uint32_t *p_initialZ)
{
/* R0 and R1 */
uint32_t Rx[2][NUM_ECC_DIGITS];
uint32_t Ry[2][NUM_ECC_DIGITS];
uint32_t z[NUM_ECC_DIGITS];
uint i, nb;
vli_set(Rx[1], p_point->x);
vli_set(Ry[1], p_point->y);
XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], p_initialZ);
for(i = vli_numBits(p_scalar) - 2; i > 0; --i)
{
nb = !vli_testBit(p_scalar, i);
XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]);
XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]);
}
nb = !vli_testBit(p_scalar, 0);
XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]);
/* Find final 1/Z value. */
vli_modSub(z, Rx[1], Rx[0], curve_p); /* X1 - X0 */
vli_modMult_fast(z, z, Ry[1-nb]); /* Yb * (X1 - X0) */
vli_modMult_fast(z, z, p_point->x); /* xP * Yb * (X1 - X0) */
vli_modInv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */
vli_modMult_fast(z, z, p_point->y); /* yP / (xP * Yb * (X1 - X0)) */
vli_modMult_fast(z, z, Rx[1-nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */
/* End 1/Z calculation */
XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]);
apply_z(Rx[0], Ry[0], z);
vli_set(p_result->x, Rx[0]);
vli_set(p_result->y, Ry[0]);
}
int ecc_make_key(EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS], uint32_t p_random[NUM_ECC_DIGITS])
{
@@ -1305,7 +1256,7 @@ int ecc_make_key(EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS], u
return 0; /* The private key cannot be 0 (mod p). */
}
EccPoint_mult(p_publicKey, &curve_G, p_privateKey);
EccPoint_mult(p_publicKey, &curve_G, p_privateKey, NULL);
return 1;
}
@@ -1341,18 +1292,18 @@ int ecc_valid_public_key(EccPoint *p_publicKey)
return 1;
}
int ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS], EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS])
int ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS], EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS], uint32_t p_random[NUM_ECC_DIGITS])
{
EccPoint l_product;
EccPoint_mult(&l_product, p_publicKey, p_privateKey);
EccPoint_mult(&l_product, p_publicKey, p_privateKey, p_random);
if(EccPoint_isZero(&l_product))
{
return 0;
}
vli_set(p_secret, l_product.x);
return 1;
}
@@ -1446,7 +1397,7 @@ int ecdsa_sign(uint32_t r[NUM_ECC_DIGITS], uint32_t s[NUM_ECC_DIGITS], uint32_t
}
/* tmp = k * G */
EccPoint_mult(&p, &curve_G, k);
EccPoint_mult(&p, &curve_G, k, NULL);
/* r = x1 (mod n) */
vli_set(r, p.x);
@@ -1470,8 +1421,13 @@ int ecdsa_sign(uint32_t r[NUM_ECC_DIGITS], uint32_t s[NUM_ECC_DIGITS], uint32_t
int ecdsa_verify(EccPoint *p_publicKey, uint32_t p_hash[NUM_ECC_DIGITS], uint32_t r[NUM_ECC_DIGITS], uint32_t s[NUM_ECC_DIGITS])
{
uint32_t u1[NUM_ECC_DIGITS], u2[NUM_ECC_DIGITS];
uint32_t Z[NUM_ECC_DIGITS];
EccPoint l_result, l_sum;
uint32_t z[NUM_ECC_DIGITS];
EccPoint l_sum;
uint32_t rx[NUM_ECC_DIGITS];
uint32_t ry[NUM_ECC_DIGITS];
uint32_t tx[NUM_ECC_DIGITS];
uint32_t ty[NUM_ECC_DIGITS];
uint32_t tz[NUM_ECC_DIGITS];
if(vli_isZero(r) || vli_isZero(s))
{ /* r, s must not be 0. */
@@ -1484,51 +1440,57 @@ int ecdsa_verify(EccPoint *p_publicKey, uint32_t p_hash[NUM_ECC_DIGITS], uint32_
}
/* Calculate u1 and u2. */
vli_modInv(Z, s, curve_n); /* Z = s^-1 */
vli_modMult(u1, p_hash, Z, curve_n); /* u1 = e/s */
vli_modMult(u2, r, Z, curve_n); /* u2 = r/s */
vli_modInv(z, s, curve_n); /* Z = s^-1 */
vli_modMult(u1, p_hash, z, curve_n); /* u1 = e/s */
vli_modMult(u2, r, z, curve_n); /* u2 = r/s */
/* Calculate l_sum = G + Q. */
vli_clear(Z);
Z[0] = 1;
EccPoint_add_mixed(&l_sum, Z, p_publicKey, Z, &curve_G);
vli_modInv(Z, Z, curve_p); /* Z1 = 1/Z */
vli_modSquare_fast(l_result.x, Z); /* tmp = 1/Z^2 */
vli_modMult_fast(l_sum.x, l_sum.x, l_result.x); /* x = x/Z^2 */
vli_modMult_fast(l_result.x, Z, l_result.x); /* tmp = 1/Z^3 */
vli_modMult_fast(l_sum.y, l_sum.y, l_result.x); /* y = y/Z^3 */
vli_set(l_sum.x, p_publicKey->x);
vli_set(l_sum.y, p_publicKey->y);
vli_set(tx, curve_G.x);
vli_set(ty, curve_G.y);
vli_modSub(z, l_sum.x, tx, curve_p); /* Z = x2 - x1 */
XYcZ_add(tx, ty, l_sum.x, l_sum.y);
vli_modInv(z, z, curve_p); /* Z = 1/Z */
apply_z(l_sum.x, l_sum.y, z);
/* Use Shamir's trick to calculate u1*G + u2*Q */
uint l_numBits = max(vli_numBits(u1), vli_numBits(u2));
vli_clear(Z);
EccPoint_clear(&l_result);
EccPoint *l_points[4] = {NULL, &curve_G, p_publicKey, &l_sum};
uint l_numBits = max(vli_numBits(u1), vli_numBits(u2));
EccPoint *l_point = l_points[(!!vli_testBit(u1, l_numBits-1)) | ((!!vli_testBit(u2, l_numBits-1)) << 1)];
vli_set(rx, l_point->x);
vli_set(ry, l_point->y);
vli_clear(z);
z[0] = 1;
int i;
for(i = l_numBits - 1; i >= 0; --i)
for(i = l_numBits - 2; i >= 0; --i)
{
EccPoint_double_projective(&l_result, Z, &l_result, Z);
EccPoint_double_jacobian(rx, ry, z);
int l_index = (!!vli_testBit(u1, i)) | ((!!vli_testBit(u2, i)) << 1);
EccPoint *l_point = l_points[l_index];
if(l_point)
{
EccPoint_add_mixed(&l_result, Z, &l_result, Z, l_point);
vli_set(tx, l_point->x);
vli_set(ty, l_point->y);
apply_z(tx, ty, z);
vli_modSub(tz, rx, tx, curve_p); /* Z = x2 - x1 */
XYcZ_add(tx, ty, rx, ry);
vli_modMult_fast(z, z, tz);
}
}
vli_modInv(Z, Z, curve_p); /* Z = 1/Z */
vli_modSquare_fast(Z, Z); /* Z = 1/Z^2 */
vli_modMult_fast(l_result.x, l_result.x, Z); /* x = x/Z^2 */
vli_modInv(z, z, curve_p); /* Z = 1/Z */
apply_z(rx, ry, z);
/* v = x1 (mod n) */
if(vli_cmp(curve_n, l_result.x) != 1)
if(vli_cmp(curve_n, rx) != 1)
{
vli_sub(l_result.x, l_result.x, curve_n);
vli_sub(rx, rx, curve_n);
}
/* Accept only if v == r. */
return (vli_cmp(l_result.x, r) == 0);
return (vli_cmp(rx, r) == 0);
}
+7 -6
View File
@@ -6,11 +6,8 @@
/* Optimization settings. Define as 1 to enable an optimization, 0 to disable it.
ECC_SQUARE_FUNC - If enabled, this will cause a specific function to be used for (scalar) squaring instead of the generic
multiplication function. Improves speed by about 8% .
ECC_USE_NAF - If enabled, this will convert the private key to a non-adjacent form before point multiplication.
Improves speed by about 14%.
*/
#define ECC_SQUARE_FUNC 1
#define ECC_USE_NAF 1
/* Inline assembly options.
Inline assembly (gcc format) is provided for selected operations for Thumb and Thumb2/ARM.
@@ -44,8 +41,8 @@ with strange assembler messages.
typedef struct EccPoint
{
uint32_t x[NUM_ECC_DIGITS];
uint32_t y[NUM_ECC_DIGITS];
uint32_t x[NUM_ECC_DIGITS];
uint32_t y[NUM_ECC_DIGITS];
} EccPoint;
/* ecc_make_key() function.
@@ -78,12 +75,16 @@ int ecc_valid_public_key(EccPoint *p_publicKey);
/* ecdh_shared_secret() function.
Compute a shared secret given your secret key and someone else's public key.
Optionally, you can provide a random multiplier for resistance to DPA attacks. The random multiplier
should probably be different for each invocation of ecdh_shared_secret().
Outputs:
p_secret - Will be filled in with the shared secret value.
Inputs:
p_publicKey - The public key of the remote party.
p_privateKey - Your private key.
p_random - An optional random number to resist DPA attacks. Pass in NULL if DPA attacks are not a concern.
Returns 1 if the shared secret was computed successfully, 0 otherwise.
@@ -91,7 +92,7 @@ Note: It is recommended that you hash the result of ecdh_shared_secret before us
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 your private key. */
int ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS], EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS]);
int ecdh_shared_secret(uint32_t p_secret[NUM_ECC_DIGITS], EccPoint *p_publicKey, uint32_t p_privateKey[NUM_ECC_DIGITS], uint32_t p_random[NUM_ECC_DIGITS]);
/* ecdsa_sign() function.
Generate an ECDSA signature for a given hash value.
+1 -1
View File
@@ -448,7 +448,7 @@ int main()
for(i=0; i<10; ++i)
{
uint64_t l_start = getTimeMs();
ecdh_shared_secret(l_shared, &g_public[i], g_private[i]);
ecdh_shared_secret(l_shared, &g_public[i], g_private[i], NULL);
uint64_t l_diff = getTimeMs() - l_start;
vli_print(l_shared);
printf("\n");
+8 -2
View File
@@ -37,6 +37,9 @@ int main()
uint32_t l_shared1[NUM_ECC_DIGITS];
uint32_t l_shared2[NUM_ECC_DIGITS];
uint32_t l_random1[NUM_ECC_DIGITS];
uint32_t l_random2[NUM_ECC_DIGITS];
randfd = open("/dev/urandom", O_RDONLY);
if(randfd == -1)
{
@@ -54,16 +57,19 @@ int main()
getRandomBytes((char *)l_secret1, NUM_ECC_DIGITS * sizeof(uint32_t));
getRandomBytes((char *)l_secret2, NUM_ECC_DIGITS * sizeof(uint32_t));
getRandomBytes((char *)l_random1, NUM_ECC_DIGITS * sizeof(uint32_t));
getRandomBytes((char *)l_random2, NUM_ECC_DIGITS * sizeof(uint32_t));
ecc_make_key(&l_Q1, l_secret1, l_secret1);
ecc_make_key(&l_Q2, l_secret2, l_secret2);
if(!ecdh_shared_secret(l_shared1, &l_Q1, l_secret2))
if(!ecdh_shared_secret(l_shared1, &l_Q1, l_secret2, l_random1))
{
printf("shared_secret() failed (1)\n");
return 1;
}
if(!ecdh_shared_secret(l_shared2, &l_Q2, l_secret1))
if(!ecdh_shared_secret(l_shared2, &l_Q2, l_secret1, l_random2))
{
printf("shared_secret() failed (2)\n");
return 1;