Browse Source

Add Little-endian BIGNUM conversions

Towards an eventual goal of opaquifying BoringSSL structs, we want
our consumers -- in this case, Android's libcore -- to not directly
manipulate BigNums; and it would be convenient for them if we would
perform the appropriate gymnastics to interpret little-endian byte
streams.

It also seems a priori a bit strange to have only big-endian varieties
of BN byte-conversions.

This CL provides little-endian equivalents of BN_bn2bin_padded
and BN_bin2bn.

BUG=97
Change-Id: I0e92483286def86d9bd71a46d6a967a3be50f80b
Reviewed-on: https://boringssl-review.googlesource.com/12641
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
kris/onging/CECPQ3_patch15
Rob Sloan 7 years ago
committed by CQ bot account: commit-bot@chromium.org
parent
commit
45573cc04d
3 changed files with 142 additions and 0 deletions
  1. +77
    -0
      crypto/bn/bn_test.cc
  2. +53
    -0
      crypto/bn/convert.c
  3. +12
    -0
      include/openssl/bn.h

+ 77
- 0
crypto/bn/bn_test.cc View File

@@ -747,6 +747,82 @@ static bool TestBN2BinPadded(BN_CTX *ctx) {
return true;
}

static bool TestLittleEndian() {
bssl::UniquePtr<BIGNUM> x(BN_new());
bssl::UniquePtr<BIGNUM> y(BN_new());
if (!x || !y) {
fprintf(stderr, "BN_new failed to malloc.\n");
return false;
}

// Test edge case at 0. Fill |out| with garbage to ensure |BN_bn2le_padded|
// wrote the result.
uint8_t out[256], zeros[256];
OPENSSL_memset(out, -1, sizeof(out));
OPENSSL_memset(zeros, 0, sizeof(zeros));
if (!BN_bn2le_padded(out, sizeof(out), x.get()) ||
OPENSSL_memcmp(zeros, out, sizeof(out))) {
fprintf(stderr, "BN_bn2le_padded failed to encode 0.\n");
return false;
}

if (!BN_le2bn(out, sizeof(out), y.get()) ||
BN_cmp(x.get(), y.get()) != 0) {
fprintf(stderr, "BN_le2bn failed to decode 0 correctly.\n");
return false;
}

// Test random numbers at various byte lengths.
for (size_t bytes = 128 - 7; bytes <= 128; bytes++) {
if (!BN_rand(x.get(), bytes * 8, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) {
ERR_print_errors_fp(stderr);
return false;
}

// Fill |out| with garbage to ensure |BN_bn2le_padded| wrote the result.
OPENSSL_memset(out, -1, sizeof(out));
if (!BN_bn2le_padded(out, sizeof(out), x.get())) {
fprintf(stderr, "BN_bn2le_padded failed to encode random value.\n");
return false;
}

// Compute the expected value by reversing the big-endian output.
uint8_t expected[sizeof(out)];
if (!BN_bn2bin_padded(expected, sizeof(expected), x.get())) {
return false;
}
for (size_t i = 0; i < sizeof(expected) / 2; i++) {
uint8_t tmp = expected[i];
expected[i] = expected[sizeof(expected) - 1 - i];
expected[sizeof(expected) - 1 - i] = tmp;
}

if (OPENSSL_memcmp(expected, out, sizeof(out))) {
fprintf(stderr, "BN_bn2le_padded failed to encode value correctly.\n");
hexdump(stderr, "Expected: ", expected, sizeof(expected));
hexdump(stderr, "Got: ", out, sizeof(out));
return false;
}

// Make sure the decoding produces the same BIGNUM.
if (!BN_le2bn(out, bytes, y.get()) ||
BN_cmp(x.get(), y.get()) != 0) {
bssl::UniquePtr<char> x_hex(BN_bn2hex(x.get())),
y_hex(BN_bn2hex(y.get()));
if (!x_hex || !y_hex) {
return false;
}
fprintf(stderr, "BN_le2bn failed to decode value correctly.\n");
fprintf(stderr, "Expected: %s\n", x_hex.get());
hexdump(stderr, "Encoding: ", out, bytes);
fprintf(stderr, "Got: %s\n", y_hex.get());
return false;
}
}

return true;
}

static int DecimalToBIGNUM(bssl::UniquePtr<BIGNUM> *out, const char *in) {
BIGNUM *raw = NULL;
int ret = BN_dec2bn(&raw, in);
@@ -1569,6 +1645,7 @@ int main(int argc, char *argv[]) {
!TestDec2BN(ctx.get()) ||
!TestHex2BN(ctx.get()) ||
!TestASC2BN(ctx.get()) ||
!TestLittleEndian() ||
!TestMPI() ||
!TestRand() ||
!TestASN1() ||


+ 53
- 0
crypto/bn/convert.c View File

@@ -118,6 +118,42 @@ BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret) {
return ret;
}

BIGNUM *BN_le2bn(const uint8_t *in, size_t len, BIGNUM *ret) {
BIGNUM *bn = NULL;
if (ret == NULL) {
bn = BN_new();
ret = bn;
}

if (ret == NULL) {
return NULL;
}

if (len == 0) {
ret->top = 0;
ret->neg = 0;
return ret;
}

/* Reserve enough space in |ret|. */
size_t num_words = ((len - 1) / BN_BYTES) + 1;
if (!bn_wexpand(ret, num_words)) {
BN_free(bn);
return NULL;
}
ret->top = num_words;

/* Make sure the top bytes will be zeroed. */
ret->d[num_words - 1] = 0;

/* We only support little-endian platforms, so we can simply memcpy the
* internal representation. */
OPENSSL_memcpy(ret->d, in, len);

bn_correct_top(ret);
return ret;
}

size_t BN_bn2bin(const BIGNUM *in, uint8_t *out) {
size_t n, i;
BN_ULONG l;
@@ -130,6 +166,23 @@ size_t BN_bn2bin(const BIGNUM *in, uint8_t *out) {
return n;
}

int BN_bn2le_padded(uint8_t *out, size_t len, const BIGNUM *in) {
/* If we don't have enough space, fail out. */
size_t num_bytes = BN_num_bytes(in);
if (len < num_bytes) {
return 0;
}

/* We only support little-endian platforms, so we can simply memcpy into the
* internal representation. */
OPENSSL_memcpy(out, in->d, num_bytes);

/* Pad out the rest of the buffer with zeroes. */
OPENSSL_memset(out + num_bytes, 0, len - num_bytes);

return 1;
}

/* constant_time_select_ulong returns |x| if |v| is 1 and |y| if |v| is 0. Its
* behavior is undefined if |v| takes any other value. */
static BN_ULONG constant_time_select_ulong(int v, BN_ULONG x, BN_ULONG y) {


+ 12
- 0
include/openssl/bn.h View File

@@ -253,6 +253,18 @@ OPENSSL_EXPORT BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret);
* number of bytes written. */
OPENSSL_EXPORT size_t BN_bn2bin(const BIGNUM *in, uint8_t *out);

/* BN_le2bn sets |*ret| to the value of |len| bytes from |in|, interpreted as
* a little-endian number, and returns |ret|. If |ret| is NULL then a fresh
* |BIGNUM| is allocated and returned. It returns NULL on allocation
* failure. */
OPENSSL_EXPORT BIGNUM *BN_le2bn(const uint8_t *in, size_t len, BIGNUM *ret);

/* BN_bn2le_padded serialises the absolute value of |in| to |out| as a
* little-endian integer, which must have |len| of space available, padding
* out the remainder of out with zeros. If |len| is smaller than |BN_num_bytes|,
* the function fails and returns 0. Otherwise, it returns 1. */
OPENSSL_EXPORT int BN_bn2le_padded(uint8_t *out, size_t len, const BIGNUM *in);

/* BN_bn2bin_padded serialises the absolute value of |in| to |out| as a
* big-endian integer. The integer is padded with leading zeros up to size
* |len|. If |len| is smaller than |BN_num_bytes|, the function fails and


Loading…
Cancel
Save