Skip to content

Commit eb79169

Browse files
author
Andy Polyakov
committed
ec/ecp_nistz256.c: improve ECDSA sign by 30-40%.
This is based on RT#3810, which added dedicated modular inversion. ECDSA verify results improves as well, but not as much. Reviewed-by: Rich Salz <rsalz@openssl.org> (Merged from #5001)
1 parent 617b49d commit eb79169

File tree

8 files changed

+1203
-45
lines changed

8 files changed

+1203
-45
lines changed

crypto/ec/asm/ecp_nistz256-x86_64.pl

+1,031-13
Large diffs are not rendered by default.

crypto/ec/ec_err.c

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static const ERR_STRING_DATA EC_str_functs[] = {
4848
"ECPKParameters_print_fp"},
4949
{ERR_PACK(ERR_LIB_EC, EC_F_ECP_NISTZ256_GET_AFFINE, 0),
5050
"ecp_nistz256_get_affine"},
51+
{ERR_PACK(ERR_LIB_EC, EC_F_ECP_NISTZ256_INV_MOD_ORD, 0),
52+
"ecp_nistz256_inv_mod_ord"},
5153
{ERR_PACK(ERR_LIB_EC, EC_F_ECP_NISTZ256_MULT_PRECOMPUTE, 0),
5254
"ecp_nistz256_mult_precompute"},
5355
{ERR_PACK(ERR_LIB_EC, EC_F_ECP_NISTZ256_POINTS_MUL, 0),

crypto/ec/ec_lcl.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ struct ec_method_st {
155155
/* custom ECDH operation */
156156
int (*ecdh_compute_key)(unsigned char **pout, size_t *poutlen,
157157
const EC_POINT *pub_key, const EC_KEY *ecdh);
158+
/* Inverse modulo order */
159+
int (*field_inverse_mod_ord)(const EC_GROUP *, BIGNUM *r, BIGNUM *x,
160+
BN_CTX *ctx);
158161
};
159162

160163
/*
@@ -520,7 +523,6 @@ void ec_GFp_nistp_points_make_affine_internal(size_t num, void *point_array,
520523
void ec_GFp_nistp_recode_scalar_bits(unsigned char *sign,
521524
unsigned char *digit, unsigned char in);
522525
#endif
523-
int ec_precompute_mont_data(EC_GROUP *);
524526
int ec_group_simple_order_bits(const EC_GROUP *group);
525527

526528
#ifdef ECP_NISTZ256_ASM
@@ -604,3 +606,6 @@ int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
604606
const uint8_t peer_public_value[32]);
605607
void X25519_public_from_private(uint8_t out_public_value[32],
606608
const uint8_t private_key[32]);
609+
610+
int EC_GROUP_do_inverse_ord(const EC_GROUP *group, BIGNUM *res,
611+
BIGNUM *x, BN_CTX *ctx);

crypto/ec/ec_lib.c

+12-1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ int EC_METHOD_get_field_type(const EC_METHOD *meth)
261261
return meth->field_type;
262262
}
263263

264+
static int ec_precompute_mont_data(EC_GROUP *);
265+
264266
int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
265267
const BIGNUM *order, const BIGNUM *cofactor)
266268
{
@@ -961,7 +963,7 @@ int EC_GROUP_have_precompute_mult(const EC_GROUP *group)
961963
* ec_precompute_mont_data sets |group->mont_data| from |group->order| and
962964
* returns one on success. On error it returns zero.
963965
*/
964-
int ec_precompute_mont_data(EC_GROUP *group)
966+
static int ec_precompute_mont_data(EC_GROUP *group)
965967
{
966968
BN_CTX *ctx = BN_CTX_new();
967969
int ret = 0;
@@ -1006,3 +1008,12 @@ int ec_group_simple_order_bits(const EC_GROUP *group)
10061008
return 0;
10071009
return BN_num_bits(group->order);
10081010
}
1011+
1012+
int EC_GROUP_do_inverse_ord(const EC_GROUP *group, BIGNUM *res,
1013+
BIGNUM *x, BN_CTX *ctx)
1014+
{
1015+
if (group->meth->field_inverse_mod_ord != NULL)
1016+
return group->meth->field_inverse_mod_ord(group, res, x, ctx);
1017+
else
1018+
return 0;
1019+
}

crypto/ec/ecdsa_ossl.c

+33-27
Original file line numberDiff line numberDiff line change
@@ -153,30 +153,33 @@ static int ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in,
153153
}
154154
while (BN_is_zero(r));
155155

156-
/* compute the inverse of k */
157-
if (EC_GROUP_get_mont_data(group) != NULL) {
158-
/*
159-
* We want inverse in constant time, therefore we utilize the fact
160-
* order must be prime and use Fermat's Little Theorem instead.
161-
*/
162-
if (!BN_set_word(X, 2)) {
163-
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
164-
goto err;
165-
}
166-
if (!BN_mod_sub(X, order, X, order, ctx)) {
167-
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
168-
goto err;
169-
}
170-
BN_set_flags(X, BN_FLG_CONSTTIME);
171-
if (!BN_mod_exp_mont_consttime
172-
(k, k, X, order, ctx, EC_GROUP_get_mont_data(group))) {
173-
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
174-
goto err;
175-
}
176-
} else {
177-
if (!BN_mod_inverse(k, k, order, ctx)) {
178-
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
179-
goto err;
156+
/* Check if optimized inverse is implemented */
157+
if (EC_GROUP_do_inverse_ord(group, k, k, ctx) == 0) {
158+
/* compute the inverse of k */
159+
if (group->mont_data != NULL) {
160+
/*
161+
* We want inverse in constant time, therefore we utilize the fact
162+
* order must be prime and use Fermats Little Theorem instead.
163+
*/
164+
if (!BN_set_word(X, 2)) {
165+
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
166+
goto err;
167+
}
168+
if (!BN_mod_sub(X, order, X, order, ctx)) {
169+
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
170+
goto err;
171+
}
172+
BN_set_flags(X, BN_FLG_CONSTTIME);
173+
if (!BN_mod_exp_mont_consttime(k, k, X, order, ctx,
174+
group->mont_data)) {
175+
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
176+
goto err;
177+
}
178+
} else {
179+
if (!BN_mod_inverse(k, k, order, ctx)) {
180+
ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
181+
goto err;
182+
}
180183
}
181184
}
182185

@@ -407,9 +410,12 @@ int ossl_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
407410
goto err;
408411
}
409412
/* calculate tmp1 = inv(S) mod order */
410-
if (!BN_mod_inverse(u2, sig->s, order, ctx)) {
411-
ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
412-
goto err;
413+
/* Check if optimized inverse is implemented */
414+
if (EC_GROUP_do_inverse_ord(group, u2, sig->s, ctx) == 0) {
415+
if (!BN_mod_inverse(u2, sig->s, order, ctx)) {
416+
ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
417+
goto err;
418+
}
413419
}
414420
/* digest -> m */
415421
i = BN_num_bits(order);

crypto/ec/ecp_nistz256.c

+117-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
/*
22
* Copyright 2014-2017 The OpenSSL Project Authors. All Rights Reserved.
33
* Copyright (c) 2014, Intel Corporation. All Rights Reserved.
4+
* Copyright (c) 2015, CloudFlare, Inc.
45
*
56
* Licensed under the OpenSSL license (the "License"). You may not use
67
* this file except in compliance with the License. You can obtain a copy
78
* in the file LICENSE in the source distribution or at
89
* https://www.openssl.org/source/license.html
910
*
10-
* Originally written by Shay Gueron (1, 2), and Vlad Krasnov (1)
11+
* Originally written by Shay Gueron (1, 2), and Vlad Krasnov (1, 3)
1112
* (1) Intel Corporation, Israel Development Center, Haifa, Israel
1213
* (2) University of Haifa, Israel
14+
* (3) CloudFlare, Inc.
1315
*
1416
* Reference:
1517
* S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with
@@ -908,7 +910,7 @@ __owur static int ecp_nistz256_mult_precompute(EC_GROUP *group, BN_CTX *ctx)
908910
*/
909911
#if defined(ECP_NISTZ256_AVX2)
910912
# if !(defined(__x86_64) || defined(__x86_64__) || \
911-
defined(_M_AMD64) || defined(_MX64)) || \
913+
defined(_M_AMD64) || defined(_M_X64)) || \
912914
!(defined(__GNUC__) || defined(_MSC_VER)) /* this is for ALIGN32 */
913915
# undef ECP_NISTZ256_AVX2
914916
# else
@@ -1495,6 +1497,117 @@ static int ecp_nistz256_window_have_precompute_mult(const EC_GROUP *group)
14951497
return HAVEPRECOMP(group, nistz256);
14961498
}
14971499

1500+
#if defined(__x86_64) || defined(__x86_64__) || \
1501+
defined(_M_AMD64) || defined(_M_X64) || \
1502+
defined(__powerpc64__) || defined(_ARCH_PP64)
1503+
/*
1504+
* Montgomery mul modulo Order(P): res = a*b*2^-256 mod Order(P)
1505+
*/
1506+
void ecp_nistz256_ord_mul_mont(BN_ULONG res[P256_LIMBS],
1507+
const BN_ULONG a[P256_LIMBS],
1508+
const BN_ULONG b[P256_LIMBS]);
1509+
void ecp_nistz256_ord_sqr_mont(BN_ULONG res[P256_LIMBS],
1510+
const BN_ULONG a[P256_LIMBS],
1511+
int rep);
1512+
1513+
static int ecp_nistz256_inv_mod_ord(const EC_GROUP *group, BIGNUM *r,
1514+
BIGNUM *x, BN_CTX *ctx)
1515+
{
1516+
/* RR = 2^512 mod ord(p256) */
1517+
static const BN_ULONG RR[P256_LIMBS] = { TOBN(0x83244c95,0xbe79eea2),
1518+
TOBN(0x4699799c,0x49bd6fa6),
1519+
TOBN(0x2845b239,0x2b6bec59),
1520+
TOBN(0x66e12d94,0xf3d95620) };
1521+
/* The constant 1 (unlike ONE that is one in Montgomery representation) */
1522+
static const BN_ULONG one[P256_LIMBS] = { TOBN(0,1),TOBN(0,0),
1523+
TOBN(0,0),TOBN(0,0) };
1524+
/* expLo - the low 128bit of the exponent we use (ord(p256) - 2),
1525+
* split into 4bit windows */
1526+
static const unsigned char expLo[32] = { 0xb,0xc,0xe,0x6,0xf,0xa,0xa,0xd,
1527+
0xa,0x7,0x1,0x7,0x9,0xe,0x8,0x4,
1528+
0xf,0x3,0xb,0x9,0xc,0xa,0xc,0x2,
1529+
0xf,0xc,0x6,0x3,0x2,0x5,0x4,0xf };
1530+
/*
1531+
* We don't use entry 0 in the table, so we omit it and address
1532+
* with -1 offset.
1533+
*/
1534+
BN_ULONG table[15][P256_LIMBS];
1535+
BN_ULONG out[P256_LIMBS], t[P256_LIMBS];
1536+
int i, ret = 0;
1537+
1538+
/*
1539+
* Catch allocation failure early.
1540+
*/
1541+
if (bn_wexpand(r, P256_LIMBS) == NULL) {
1542+
ECerr(EC_F_ECP_NISTZ256_INV_MOD_ORD, ERR_R_BN_LIB);
1543+
goto err;
1544+
}
1545+
1546+
if ((BN_num_bits(x) > 256) || BN_is_negative(x)) {
1547+
BIGNUM *tmp;
1548+
1549+
if ((tmp = BN_CTX_get(ctx)) == NULL
1550+
|| !BN_nnmod(tmp, x, group->order, ctx)) {
1551+
ECerr(EC_F_ECP_NISTZ256_INV_MOD_ORD, ERR_R_BN_LIB);
1552+
goto err;
1553+
}
1554+
x = tmp;
1555+
}
1556+
1557+
if (!ecp_nistz256_bignum_to_field_elem(t, x)) {
1558+
ECerr(EC_F_ECP_NISTZ256_INV_MOD_ORD, EC_R_COORDINATES_OUT_OF_RANGE);
1559+
goto err;
1560+
}
1561+
1562+
ecp_nistz256_ord_mul_mont(table[0], t, RR);
1563+
for (i = 2; i < 16; i += 2) {
1564+
ecp_nistz256_ord_sqr_mont(table[i-1], table[i/2-1], 1);
1565+
ecp_nistz256_ord_mul_mont(table[i], table[i-1], table[0]);
1566+
}
1567+
1568+
/*
1569+
* The top 128bit of the exponent are highly redudndant, so we
1570+
* perform an optimized flow
1571+
*/
1572+
ecp_nistz256_ord_sqr_mont(t, table[15-1], 4); /* f0 */
1573+
ecp_nistz256_ord_mul_mont(t, t, table[15-1]); /* ff */
1574+
1575+
ecp_nistz256_ord_sqr_mont(out, t, 8); /* ff00 */
1576+
ecp_nistz256_ord_mul_mont(out, out, t); /* ffff */
1577+
1578+
ecp_nistz256_ord_sqr_mont(t, out, 16); /* ffff0000 */
1579+
ecp_nistz256_ord_mul_mont(t, t, out); /* ffffffff */
1580+
1581+
ecp_nistz256_ord_sqr_mont(out, t, 64); /* ffffffff0000000000000000 */
1582+
ecp_nistz256_ord_mul_mont(out, out, t); /* ffffffff00000000ffffffff */
1583+
1584+
ecp_nistz256_ord_sqr_mont(out, out, 32); /* ffffffff00000000ffffffff00000000 */
1585+
ecp_nistz256_ord_mul_mont(out, out, t); /* ffffffff00000000ffffffffffffffff */
1586+
1587+
/*
1588+
* The bottom 128 bit of the exponent are easier done with a table
1589+
*/
1590+
for(i = 0; i < 32; i++) {
1591+
ecp_nistz256_ord_sqr_mont(out, out, 4);
1592+
/* The exponent is public, no need in constant-time access */
1593+
ecp_nistz256_ord_mul_mont(out, out, table[expLo[i]-1]);
1594+
}
1595+
ecp_nistz256_ord_mul_mont(out, out, one);
1596+
1597+
/*
1598+
* Can't fail, but check return code to be consistent anyway.
1599+
*/
1600+
if (!bn_set_words(r, out, P256_LIMBS))
1601+
goto err;
1602+
1603+
ret = 1;
1604+
err:
1605+
return ret;
1606+
}
1607+
#else
1608+
# define ecp_nistz256_inv_mod_ord NULL
1609+
#endif
1610+
14981611
const EC_METHOD *EC_GFp_nistz256_method(void)
14991612
{
15001613
static const EC_METHOD ret = {
@@ -1544,7 +1657,8 @@ const EC_METHOD *EC_GFp_nistz256_method(void)
15441657
ec_key_simple_generate_public_key,
15451658
0, /* keycopy */
15461659
0, /* keyfinish */
1547-
ecdh_simple_compute_key
1660+
ecdh_simple_compute_key,
1661+
ecp_nistz256_inv_mod_ord /* can be #defined-ed NULL */
15481662
};
15491663

15501664
return &ret;

crypto/err/openssl.txt

+1
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ EC_F_ECPARAMETERS_PRINT_FP:148:ECParameters_print_fp
458458
EC_F_ECPKPARAMETERS_PRINT:149:ECPKParameters_print
459459
EC_F_ECPKPARAMETERS_PRINT_FP:150:ECPKParameters_print_fp
460460
EC_F_ECP_NISTZ256_GET_AFFINE:240:ecp_nistz256_get_affine
461+
EC_F_ECP_NISTZ256_INV_MOD_ORD:275:ecp_nistz256_inv_mod_ord
461462
EC_F_ECP_NISTZ256_MULT_PRECOMPUTE:243:ecp_nistz256_mult_precompute
462463
EC_F_ECP_NISTZ256_POINTS_MUL:241:ecp_nistz256_points_mul
463464
EC_F_ECP_NISTZ256_PRE_COMP_NEW:244:ecp_nistz256_pre_comp_new

include/openssl/ecerr.h

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ int ERR_load_EC_strings(void);
5050
# define EC_F_ECPKPARAMETERS_PRINT 149
5151
# define EC_F_ECPKPARAMETERS_PRINT_FP 150
5252
# define EC_F_ECP_NISTZ256_GET_AFFINE 240
53+
# define EC_F_ECP_NISTZ256_INV_MOD_ORD 275
5354
# define EC_F_ECP_NISTZ256_MULT_PRECOMPUTE 243
5455
# define EC_F_ECP_NISTZ256_POINTS_MUL 241
5556
# define EC_F_ECP_NISTZ256_PRE_COMP_NEW 244

0 commit comments

Comments
 (0)