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
@@ -747,6 +747,82 @@ static bool TestBN2BinPadded(BN_CTX *ctx) { | |||||
return true; | 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) { | static int DecimalToBIGNUM(bssl::UniquePtr<BIGNUM> *out, const char *in) { | ||||
BIGNUM *raw = NULL; | BIGNUM *raw = NULL; | ||||
int ret = BN_dec2bn(&raw, in); | int ret = BN_dec2bn(&raw, in); | ||||
@@ -1569,6 +1645,7 @@ int main(int argc, char *argv[]) { | |||||
!TestDec2BN(ctx.get()) || | !TestDec2BN(ctx.get()) || | ||||
!TestHex2BN(ctx.get()) || | !TestHex2BN(ctx.get()) || | ||||
!TestASC2BN(ctx.get()) || | !TestASC2BN(ctx.get()) || | ||||
!TestLittleEndian() || | |||||
!TestMPI() || | !TestMPI() || | ||||
!TestRand() || | !TestRand() || | ||||
!TestASN1() || | !TestASN1() || | ||||
@@ -118,6 +118,42 @@ BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret) { | |||||
return 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 BN_bn2bin(const BIGNUM *in, uint8_t *out) { | ||||
size_t n, i; | size_t n, i; | ||||
BN_ULONG l; | BN_ULONG l; | ||||
@@ -130,6 +166,23 @@ size_t BN_bn2bin(const BIGNUM *in, uint8_t *out) { | |||||
return n; | 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 | /* 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. */ | * behavior is undefined if |v| takes any other value. */ | ||||
static BN_ULONG constant_time_select_ulong(int v, BN_ULONG x, BN_ULONG y) { | static BN_ULONG constant_time_select_ulong(int v, BN_ULONG x, BN_ULONG y) { | ||||
@@ -253,6 +253,18 @@ OPENSSL_EXPORT BIGNUM *BN_bin2bn(const uint8_t *in, size_t len, BIGNUM *ret); | |||||
* number of bytes written. */ | * number of bytes written. */ | ||||
OPENSSL_EXPORT size_t BN_bn2bin(const BIGNUM *in, uint8_t *out); | 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 | /* 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 | * 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 | * |len|. If |len| is smaller than |BN_num_bytes|, the function fails and | ||||