Skip to content

Commit 171470e

Browse files
committed
Expose API for constant time point multiplication
1 parent 955774c commit 171470e

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

include/secp256k1.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,24 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover_compact(
217217
int recid
218218
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
219219

220+
/** Compute an EC Diffie-Hellman secret in constant time
221+
* Returns: 1: exponentiation was successful
222+
* -1: scalar was zero
223+
* -2: scalar overflow
224+
* -3: invalid input point
225+
* In: scalar: a 32-byte scalar with which to multiply the point
226+
* point: pointer to 33 or 65 byte array containing an EC point
227+
* pointlen: length of the point array
228+
* Out: result: a 32-byte array which will be populated by an ECDH
229+
* secret computed from the point and scalar
230+
*/
231+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
232+
unsigned char *result,
233+
unsigned char *point,
234+
int *pointlen,
235+
const unsigned char *scalar
236+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
237+
220238
/** Verify an ECDSA secret key.
221239
* Returns: 1: secret key is valid
222240
* 0: secret key is invalid

src/secp256k1.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,50 @@ int secp256k1_ecdsa_recover_compact(const secp256k1_context_t* ctx, const unsign
225225
return ret;
226226
}
227227

228+
int secp256k1_ecdh(unsigned char *result, unsigned char *point, int *pointlen, const unsigned char *scalar) {
229+
int ret = 0;
230+
int overflow = 0;
231+
secp256k1_gej_t res;
232+
secp256k1_ge_t pt;
233+
secp256k1_scalar_t s;
234+
DEBUG_CHECK(point != NULL);
235+
DEBUG_CHECK(pointlen != NULL);
236+
DEBUG_CHECK(scalar != NULL);
237+
238+
if (secp256k1_eckey_pubkey_parse(&pt, point, *pointlen)) {
239+
secp256k1_scalar_set_b32(&s, scalar, &overflow);
240+
if (secp256k1_scalar_is_zero(&s)) {
241+
ret = -1;
242+
} else if (overflow) {
243+
ret = -2;
244+
} else {
245+
unsigned char x[32];
246+
unsigned char y[1];
247+
secp256k1_sha256_t sha;
248+
249+
secp256k1_point_multiply(&res, &pt, &s);
250+
secp256k1_ge_set_gej(&pt, &res);
251+
/* Compute a hash of the point in compressed form
252+
* Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not
253+
* expect its output to be secret and has a timing sidechannel. */
254+
secp256k1_fe_normalize(&pt.x);
255+
secp256k1_fe_normalize(&pt.y);
256+
secp256k1_fe_get_b32(x, &pt.x);
257+
y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y);
258+
259+
secp256k1_sha256_initialize(&sha);
260+
secp256k1_sha256_write(&sha, y, sizeof(y));
261+
secp256k1_sha256_write(&sha, x, sizeof(x));
262+
secp256k1_sha256_finalize(&sha, result);
263+
ret = 1;
264+
}
265+
} else {
266+
ret = -3;
267+
}
268+
secp256k1_scalar_clear(&s);
269+
return ret;
270+
}
271+
228272
int secp256k1_ec_seckey_verify(const secp256k1_context_t* ctx, const unsigned char *seckey) {
229273
secp256k1_scalar_t sec;
230274
int ret;

src/tests.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,13 +1348,50 @@ void ecdh_chain_multiply(void) {
13481348
ge_equals_gej(&res, &expected_point);
13491349
}
13501350

1351+
void ecdh_generator_basepoint(void) {
1352+
secp256k1_ge_t gen = secp256k1_ge_const_g;
1353+
unsigned char point[33];
1354+
unsigned char point2[33];
1355+
int pointlen = sizeof(point), point2len = sizeof(point2);
1356+
int i;
1357+
1358+
/* Check against pubkey creation when the basepoint is the generator */
1359+
for (i = 0; i < 100; ++i) {
1360+
secp256k1_sha256_t sha;
1361+
unsigned char s_b32[32];
1362+
unsigned char output_ecdh[32];
1363+
unsigned char output_ser[32];
1364+
secp256k1_scalar_t s;
1365+
1366+
random_scalar_order(&s);
1367+
secp256k1_scalar_get_b32(s_b32, &s);
1368+
1369+
/* compute using ECDH function */
1370+
secp256k1_eckey_pubkey_serialize(&gen, point, &pointlen, 1);
1371+
CHECK(secp256k1_ecdh(output_ecdh, point, &pointlen, s_b32) == 1);
1372+
/* compute "explicitly" */
1373+
secp256k1_eckey_pubkey_serialize(&gen, point2, &point2len, 1);
1374+
CHECK(secp256k1_ec_pubkey_create(ctx, point2, &point2len, s_b32, 1) == 1);
1375+
1376+
secp256k1_sha256_initialize(&sha);
1377+
secp256k1_sha256_write(&sha, point2, sizeof(point2));
1378+
secp256k1_sha256_finalize(&sha, output_ser);
1379+
/* compare */
1380+
CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0);
1381+
}
1382+
}
1383+
13511384
void run_ecdh_tests(void) {
13521385
ecdh_mult_zero_one();
13531386
ecdh_random_mult();
13541387
ecdh_commutativity();
13551388
ecdh_chain_multiply();
13561389
}
13571390

1391+
void run_ecdh_api_tests(void) {
1392+
ecdh_generator_basepoint();
1393+
}
1394+
13581395
/***** ECMULT TESTS *****/
13591396

13601397
void run_ecmult_chain(void) {
@@ -2448,6 +2485,7 @@ int main(int argc, char **argv) {
24482485

24492486
/* ecdh tests */
24502487
run_ecdh_tests();
2488+
run_ecdh_api_tests();
24512489

24522490
/* ecdsa tests */
24532491
run_random_pubkeys();

0 commit comments

Comments
 (0)