Skip to content

Commit

Permalink
Use Rust for exponentiation with public exponents.
Browse files Browse the repository at this point in the history
  • Loading branch information
briansmith committed Jan 2, 2017
1 parent d420473 commit 7c2fafe
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 514 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ include = [
"src/polyfill.rs",
"src/rand.rs",
"src/rsa/bigint.rs",
"src/rsa/bigint_elem_exp_tests.txt",
"src/rsa/bigint_elem_exp_vartime_tests.txt",
"src/rsa/blinding.rs",
"src/rsa/padding.rs",
"src/rsa/random.rs",
Expand Down
25 changes: 25 additions & 0 deletions crypto/bn/bn.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
#include "internal.h"


/* Avoid -Wmissing-prototypes warnings. */

uint64_t GFp_BN_get_positive_u64(const BIGNUM *bn);


BIGNUM *GFp_BN_new(void) {
BIGNUM *bn = OPENSSL_malloc(sizeof(BIGNUM));

Expand Down Expand Up @@ -189,6 +194,26 @@ int GFp_BN_one(BIGNUM *bn) {
return GFp_BN_set_word(bn, 1);
}

/* GFp_BN_get_positive_u64 returns the value of |bn| if the value is in
* [1, 2**64). Otherwise it returns 0 to indicate an error occurred. */
uint64_t GFp_BN_get_positive_u64(const BIGNUM *bn) {
if (bn->top > 64 / BN_BITS2) {
return 0;
}
uint64_t r = 0;
if (bn->top > 0) {
r = bn->d[0];
}
#if BN_BITS2 == 32
if (bn->top > 1) {
r |= ((uint64_t)bn->d[1]) << BN_BITS2;
}
#elif BN_BITS2 != 64
#error BN_BITS2 is not 32 or 64.
#endif
return r;
}

int GFp_BN_set_word(BIGNUM *bn, BN_ULONG value) {
if (value == 0) {
GFp_BN_zero(bn);
Expand Down
30 changes: 3 additions & 27 deletions crypto/bn/bn_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,6 @@ static bool TestModExp(FileTest *t) {
return false;
}

// |GFp_BN_mod_exp_mont_vartime| requires the input to already be reduced
// mod |m|. |GFp_BN_mod_exp_mont_consttime| doesn't have the same
// requirement simply because we haven't gotten around to it yet.
int expected_ok = GFp_BN_cmp(a.get(), m.get()) < 0;

ScopedBN_MONT_CTX mont(GFp_BN_MONT_CTX_new());
Expand All @@ -465,19 +462,12 @@ static bool TestModExp(FileTest *t) {
return false;
}

int ok = GFp_BN_mod_exp_mont_vartime(ret.get(), a.get(), e.get(),
mont.get());
int ok =
GFp_BN_mod_exp_mont_consttime(ret.get(), a.get(), e.get(), mont.get());
if (ok != expected_ok) {
return false;
}
if ((ok &&
!ExpectBIGNUMsEqual(t, "A ^ E (mod M) (Montgomery)", mod_exp.get(),
ret.get()))) {
return false;
}

if (!GFp_BN_mod_exp_mont_consttime(ret.get(), a.get(), e.get(),
mont.get()) ||
if (ok &&
!ExpectBIGNUMsEqual(t, "A ^ E (mod M) (constant-time)", mod_exp.get(),
ret.get())) {
return false;
Expand Down Expand Up @@ -831,14 +821,6 @@ static bool TestExpModRejectUnreduced() {
return false;
}

if (base_value >= mod_value &&
GFp_BN_mod_exp_mont_vartime(r.get(), base.get(), exp.get(),
mont.get())) {
fprintf(stderr, "GFp_BN_mod_exp_mont_vartime(%d, %d, %d) succeeded!\n",
(int)base_value, (int)exp_value, (int)mod_value);
return false;
}

if (base_value >= mod_value &&
GFp_BN_mod_exp_mont_consttime(r.get(), base.get(), exp.get(),
mont.get())) {
Expand All @@ -849,12 +831,6 @@ static bool TestExpModRejectUnreduced() {

GFp_BN_set_negative(base.get(), 1);

if (GFp_BN_mod_exp_mont_vartime(r.get(), base.get(), exp.get(),
mont.get())) {
fprintf(stderr, "GFp_BN_mod_exp_mont_vartime(%d, %d, %d) succeeded!\n",
-(int)base_value, (int)exp_value, (int)mod_value);
return false;
}
if (GFp_BN_mod_exp_mont_consttime(r.get(), base.get(), exp.get(),
mont.get())) {
fprintf(stderr, "GFp_BN_mod_exp_mont_consttime(%d, %d, %d) succeeded!\n",
Expand Down
19 changes: 19 additions & 0 deletions crypto/bn/cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@

#include <openssl/bn.h>

#include "openssl/mem.h"

#include "internal.h"


/* Avoid -Wmissing-prototypes warnings. */
int GFp_BN_equal_consttime(const BIGNUM *a, const BIGNUM *b);


int GFp_BN_ucmp(const BIGNUM *a, const BIGNUM *b) {
int i;
BN_ULONG t1, t2, *ap, *bp;
Expand Down Expand Up @@ -163,3 +169,16 @@ int GFp_BN_is_one(const BIGNUM *bn) {
int GFp_BN_is_odd(const BIGNUM *bn) {
return bn->top > 0 && (bn->d[0] & 1) == 1;
}

int GFp_BN_equal_consttime(const BIGNUM *a, const BIGNUM *b) {
if (a->top != b->top) {
return 0;
}

int limbs_are_equal =
GFp_memcmp(a->d, b->d, (size_t)a->top * sizeof(a->d[0])) == 0;

int signs_are_equal = a->neg == b->neg;

return limbs_are_equal && signs_are_equal;
}
186 changes: 0 additions & 186 deletions crypto/bn/exponentiation.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,192 +134,6 @@ int GFp_bn_from_montgomery(BN_ULONG *rp, const BN_ULONG *ap,
const BN_ULONG *n0, int num);
#endif

/* maximum precomputation table size for *variable* sliding windows */
#define TABLE_SIZE 32

/* GFp_BN_window_bits_for_exponent_size -- macro for sliding window mod_exp
* functions
*
* For window size 'w' (w >= 2) and a random 'b' bits exponent, the number of
* multiplications is a constant plus on average
*
* 2^(w-1) + (b-w)/(w+1);
*
* here 2^(w-1) is for precomputing the table (we actually need entries only
* for windows that have the lowest bit set), and (b-w)/(w+1) is an
* approximation for the expected number of w-bit windows, not counting the
* first one.
*
* Thus we should use
*
* w >= 6 if b > 671
* w = 5 if 671 > b > 239
* w = 4 if 239 > b > 79
* w = 3 if 79 > b > 23
* w <= 2 if 23 > b
*
* (with draws in between). Very small exponents are often selected
* with low Hamming weight, so we use w = 1 for b <= 23. */
#define GFp_BN_window_bits_for_exponent_size(b) \
((b) > 671 ? 6 : \
(b) > 239 ? 5 : \
(b) > 79 ? 4 : \
(b) > 23 ? 3 : 1)

/* |p| must be positive. */
int GFp_BN_mod_exp_mont_vartime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
const BN_MONT_CTX *mont) {
const BIGNUM *m = &mont->N;

int j, bits, ret = 0, wstart, window;
int start = 1;
BIGNUM *val[TABLE_SIZE];
size_t val_len = 0;

/* XXX: This should be after the |BN_R_INPUT_NOT_REDUCED| check, but it isn't
* in order to allow the |test_exp_mod_zero| test to keep working. Hopefully
* we can simplify the users of this code so that it is clear that what
* |test_exp_mod_zero| tests doesn't need to be supported. */
bits = GFp_BN_num_bits(p);
assert(bits > 0);

if (a->neg || GFp_BN_ucmp(a, m) >= 0) {
OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
return 0;
}

BIGNUM d;
GFp_BN_init(&d);

BIGNUM r;
GFp_BN_init(&r);

val[0] = GFp_BN_new();
if (val[0] == NULL) {
goto err;
}
++val_len;

if (GFp_BN_is_zero(a)) {
GFp_BN_zero(rr);
ret = 1;
goto err;
}
if (!GFp_BN_to_mont(val[0], a, mont)) {
goto err; /* 1 */
}

window = GFp_BN_window_bits_for_exponent_size(bits);
if (window > 1) {
if (!GFp_BN_mod_mul_mont(&d, val[0], val[0], mont)) {
goto err; /* 2 */
}
j = 1 << (window - 1);
for (int i = 1; i < j; i++) {
val[i] = GFp_BN_new();
if (val[i] == NULL) {
goto err;
}
++val_len;
if (!GFp_BN_mod_mul_mont(val[i], val[i - 1], &d, mont)) {
goto err;
}
}
}

start = 1; /* This is used to avoid multiplication etc
* when there is only the value '1' in the
* buffer. */
wstart = bits - 1; /* The top bit of the window */

j = m->top; /* borrow j */
if (m->d[j - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
if (GFp_bn_wexpand(&r, j) == NULL) {
goto err;
}
/* 2^(top*BN_BITS2) - m */
r.d[0] = (0 - m->d[0]) & BN_MASK2;
for (int i = 1; i < j; i++) {
r.d[i] = (~m->d[i]) & BN_MASK2;
}
r.top = j;
/* Upper words will be zero if the corresponding words of 'm'
* were 0xfff[...], so decrement r.top accordingly. */
GFp_bn_correct_top(&r);
} else if (!GFp_BN_set_word(&r, 1) ||
!GFp_BN_to_mont(&r, &r, mont)) {
goto err;
}

for (;;) {
int wvalue; /* The 'value' of the window */
int wend; /* The bottom bit of the window */

if (GFp_BN_is_bit_set(p, wstart) == 0) {
if (!start && !GFp_BN_mod_mul_mont(&r, &r, &r, mont)) {
goto err;
}
if (wstart == 0) {
break;
}
wstart--;
continue;
}

/* We now have wstart on a 'set' bit, we now need to work out how bit a
* window to do. To do this we need to scan forward until the last set bit
* before the end of the window */
wvalue = 1;
wend = 0;
for (int i = 1; i < window; i++) {
if (wstart - i < 0) {
break;
}
if (GFp_BN_is_bit_set(p, wstart - i)) {
wvalue <<= (i - wend);
wvalue |= 1;
wend = i;
}
}

/* wend is the size of the current window */
j = wend + 1;
/* add the 'bytes above' */
if (!start) {
for (int i = 0; i < j; i++) {
if (!GFp_BN_mod_mul_mont(&r, &r, &r, mont)) {
goto err;
}
}
}

/* wvalue will be an odd number < 2^window */
if (!GFp_BN_mod_mul_mont(&r, &r, val[wvalue >> 1], mont)) {
goto err;
}

/* move the 'window' down further */
wstart -= wend + 1;
start = 0;
if (wstart < 0) {
break;
}
}

if (!GFp_BN_from_mont(rr, &r, mont)) {
goto err;
}
ret = 1;

err:
for (size_t i = 0; i < val_len; ++i) {
GFp_BN_free(val[i]);
}
GFp_BN_free(&r);
GFp_BN_free(&d);
return ret;
}

/* GFp_BN_mod_exp_mont_consttime() stores the precomputed powers in a specific
* layout so that accessing any of these table values shows the same access
* pattern as far as cache lines are concerned. The following functions are
Expand Down
Loading

0 comments on commit 7c2fafe

Please sign in to comment.