From 85a4807f94580b9b666b3e24fff5752515470b1c Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Mon, 1 Feb 2016 00:02:05 +0000 Subject: [PATCH] New BN functions. Add new function BN_bn2binpad() which checks the length of the output buffer and pads the result with zeroes if necessary. New functions BN_bn2lebinpad() and BN_lebin2bn() which use little endian format. Reviewed-by: Rich Salz --- crypto/bn/bn_lib.c | 94 ++++++++++++++++++++++++++++++++++++++-- crypto/pem/pvkfmt.c | 42 +++--------------- doc/crypto/BN_bn2bin.pod | 20 ++++++++- include/openssl/bn.h | 3 ++ 4 files changed, 117 insertions(+), 42 deletions(-) diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c index cd8b1dc3bf..dd07d19b07 100644 --- a/crypto/bn/bn_lib.c +++ b/crypto/bn/bn_lib.c @@ -575,18 +575,104 @@ BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret) } /* ignore negative */ -int BN_bn2bin(const BIGNUM *a, unsigned char *to) +static int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen) { - int n, i; + int i; BN_ULONG l; bn_check_top(a); - n = i = BN_num_bytes(a); + i = BN_num_bytes(a); + if (tolen == -1) + tolen = i; + else if (tolen < i) + return -1; + /* Add leading zeroes if necessary */ + if (tolen > i) { + memset(to, 0, tolen - i); + to += tolen - i; + } while (i--) { l = a->d[i / BN_BYTES]; *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; } - return (n); + return tolen; +} + +int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen) +{ + if (tolen < 0) + return -1; + return bn2binpad(a, to, tolen); +} + +int BN_bn2bin(const BIGNUM *a, unsigned char *to) +{ + return bn2binpad(a, to, -1); +} + +BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret) +{ + unsigned int i, m; + unsigned int n; + BN_ULONG l; + BIGNUM *bn = NULL; + + if (ret == NULL) + ret = bn = BN_new(); + if (ret == NULL) + return (NULL); + bn_check_top(ret); + s += len - 1; + /* Skip trailing zeroes. */ + for ( ; len > 0 && *s == 0; s--, len--) + continue; + n = len; + if (n == 0) { + ret->top = 0; + return ret; + } + i = ((n - 1) / BN_BYTES) + 1; + m = ((n - 1) % (BN_BYTES)); + if (bn_wexpand(ret, (int)i) == NULL) { + BN_free(bn); + return NULL; + } + ret->top = i; + ret->neg = 0; + l = 0; + while (n--) { + l = (l << 8L) | *(s--); + if (m-- == 0) { + ret->d[--i] = l; + l = 0; + m = BN_BYTES - 1; + } + } + /* + * need to call this due to clear byte at top if avoiding having the top + * bit set (-ve number) + */ + bn_correct_top(ret); + return ret; +} + +int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen) +{ + int i; + BN_ULONG l; + bn_check_top(a); + i = BN_num_bytes(a); + if (tolen < i) + return -1; + /* Add trailing zeroes if necessary */ + if (tolen > i) + memset(to + i, 0, tolen - i); + to += i - 1; + while (i--) { + l = a->d[i / BN_BYTES]; + *(to--) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; + } + return tolen; } int BN_ucmp(const BIGNUM *a, const BIGNUM *b) diff --git a/crypto/pem/pvkfmt.c b/crypto/pem/pvkfmt.c index 625b488e34..736eb27b69 100644 --- a/crypto/pem/pvkfmt.c +++ b/crypto/pem/pvkfmt.c @@ -93,23 +93,11 @@ static unsigned int read_ledword(const unsigned char **in) static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r) { - const unsigned char *p; - unsigned char *tmpbuf, *q; - unsigned int i; - p = *in + nbyte - 1; - tmpbuf = OPENSSL_malloc(nbyte); - if (tmpbuf == NULL) - return 0; - q = tmpbuf; - for (i = 0; i < nbyte; i++) - *q++ = *p--; - *r = BN_bin2bn(tmpbuf, nbyte, NULL); - OPENSSL_free(tmpbuf); - if (*r) { - *in += nbyte; - return 1; - } else + *r = BN_lebin2bn(*in, nbyte, NULL); + if (*r == NULL) return 0; + *in += nbyte; + return 1; } /* Convert private key blob to EVP_PKEY: RSA and DSA keys supported */ @@ -417,26 +405,8 @@ static void write_ledword(unsigned char **out, unsigned int dw) static void write_lebn(unsigned char **out, const BIGNUM *bn, int len) { - int nb, i; - unsigned char *p = *out, *q, c; - nb = BN_num_bytes(bn); - BN_bn2bin(bn, p); - q = p + nb - 1; - /* In place byte order reversal */ - for (i = 0; i < nb / 2; i++) { - c = *p; - *p++ = *q; - *q-- = c; - } - *out += nb; - /* Pad with zeroes if we have to */ - if (len > 0) { - len -= nb; - if (len > 0) { - memset(*out, 0, len); - *out += len; - } - } + BN_bn2lebinpad(bn, *out, len); + *out += len; } static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *magic); diff --git a/doc/crypto/BN_bn2bin.pod b/doc/crypto/BN_bn2bin.pod index dd1007d2ce..cbd5d340e1 100644 --- a/doc/crypto/BN_bn2bin.pod +++ b/doc/crypto/BN_bn2bin.pod @@ -2,16 +2,21 @@ =head1 NAME -BN_bn2bin, BN_bin2bn, BN_bn2hex, BN_bn2dec, BN_hex2bn, BN_dec2bn, -BN_print, BN_print_fp, BN_bn2mpi, BN_mpi2bn - format conversions +BN_bn2bin, BN_bin2bn, BN_bn2lebinpad, BN_lebin2bn, BN_bn2hex, BN_bn2dec, +BN_hex2bn, BN_dec2bn, BN_print, BN_print_fp, BN_bn2mpi, +BN_mpi2bn - format conversions =head1 SYNOPSIS #include int BN_bn2bin(const BIGNUM *a, unsigned char *to); + int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen); BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); + int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen); + BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret); + char *BN_bn2hex(const BIGNUM *a); char *BN_bn2dec(const BIGNUM *a); int BN_hex2bn(BIGNUM **a, const char *str); @@ -29,10 +34,18 @@ BN_bn2bin() converts the absolute value of B into big-endian form and stores it at B. B must point to BN_num_bytes(B) bytes of memory. +BN_bn2binpad() also converts the absolute value of B into big-endian form +and stores it at B. B indicates the length of the output buffer +B. The result is padded with zeroes if necessary. If B is less than +BN_num_bytes(B) an error is returned. + BN_bin2bn() converts the positive integer in big-endian form of length B at B into a B and places it in B. If B is NULL, a new B is created. +BN_bn2lebinpad() and BN_bin2lbn() are identical to BN_bn2binpad() and +BN_bin2bn() except the buffer is in little-endian format. + BN_bn2hex() and BN_bn2dec() return printable strings containing the hexadecimal and decimal encoding of B respectively. For negative numbers, the string is prefaced with a leading '-'. The string must be @@ -67,6 +80,9 @@ if B is NULL. BN_bn2bin() returns the length of the big-endian number placed at B. BN_bin2bn() returns the B, NULL on error. +BN_bn2binpad() returns the number of bytes written or -1 if the supplied +buffer is too small. + BN_bn2hex() and BN_bn2dec() return a null-terminated string, or NULL on error. BN_hex2bn() and BN_dec2bn() return the number's length in hexadecimal or decimal digits, and 0 on error. diff --git a/include/openssl/bn.h b/include/openssl/bn.h index 37baef347f..9807b2c684 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -270,6 +270,9 @@ BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); void BN_swap(BIGNUM *a, BIGNUM *b); BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); int BN_bn2bin(const BIGNUM *a, unsigned char *to); +int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen); +BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen); BIGNUM *BN_mpi2bn(const unsigned char *s, int len, BIGNUM *ret); int BN_bn2mpi(const BIGNUM *a, unsigned char *to); int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); -- 2.34.1