blob: f12829d93c59de1ae53febaf8f73b39795da1ccd [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*/
#include <assert.h>
#include <mpa.h>
#include <trace.h>
/*
* Big #ifdef to get rid of string conversion routines
*/
#if defined(MPA_INCLUDE_STRING_CONVERSION)
/*************************************************************
*
* HELPERS
*
*************************************************************/
/* --------------------------------------------------------------------
* Function: __mpa_isspace
*
* Returns 1 if c is a while space character
*/
static int __mpa_isspace(char c)
{
return c == '_' || /* allow underscore which makes long hex */
/* numbers easier to read */
c == ' ' || /* space */
c == '\n' || /* new line */
c == '\r' || /* carriage return */
c == '\t'; /* tab */
}
/* --------------------------------------------------------------------
* Function: __mpa_is_char_in_base
*
* Returns 1 if c is either a white space char or a char in the current base,
* and 0 otherwise.
*/
static int __mpa_is_char_in_base(int base, int c)
{
if (__mpa_isspace(c))
return 1;
switch (base) {
case 10:
return (c >= '0') && (c <= '9');
case 16:
return ((c >= '0') && (c <= '9')) ||
((c >= 'A') && (c <= 'F')) ||
((c >= 'a') && (c <= 'f'));
default:
return 0;
}
}
/* --------------------------------------------------------------------
* Function: __mpa_digit_value
*
* Returns the integer value of the hexadecimal character c.
*/
static int __mpa_digit_value(int c)
{
if ((c >= '0') && (c <= '9'))
return c - '0';
if ((c >= 'A') && (c <= 'F'))
return c - 'A' + 10;
if ((c >= 'a') && (c <= 'f'))
return c - 'a' + 10;
/* defensive */
return 0;
}
/* --------------------------------------------------------------------
* Function: __mpa_digitstr_to_binary_wsize
*
* Returns the maximum number of words needed to binary represent a number
* consisting of "digits" digits and each digits is in base "base".
*/
static mpa_word_t __mpa_digitstr_to_binary_wsize_base_16(int digits)
{
return (digits + 7) >> 3;
}
/* --------------------------------------------------------------------
* Function: __mpa_nibble_to_char
*
* caseing = 1 is lower case, 0 is uppercase
*/
static char __mpa_nibble_to_char(mpa_word_t c, int caseing)
{
c &= 0xf;
if (c < 0xa)
return '0' + (char)c;
return caseing == 0 ? 'A' - 0xA + (char)c: 'a' - 0xa + (char)c;
}
/* --------------------------------------------------------------------
* Function: __mpa_word_to_hexstr
*
* caseing 8= 1 is lower case, 0 is uppercase
*/
static void __mpa_word_to_hexstr(char *str, mpa_word_t w, int caseing)
{
int i;
for (i = NIBBLES_PER_WORD; i > 0; i--) {
str[i - 1] =
__mpa_nibble_to_char(NIBBLE_OF_WORD(i - 1, w), caseing);
}
}
/* --------------------------------------------------------------------
* Function: __mpa_mpanum_to_hexstr
*
* caseing = 1 is lower case, 0 is uppercase
*/
static int __mpa_mpanum_to_hexstr(char *str, int caseing, const mpanum n)
{
int d_idx;
char digits[NIBBLES_PER_WORD];
int i;
char *cptr;
int hex_digits;
/* get high word with data in, watch out for zero case */
d_idx = __mpanum_size(n);
if (d_idx == 0) {
*str++ = '0';
*str = '\0';
return 1;
}
d_idx--;
cptr = str;
/* the msw is special, since if we should not print leading zeros.
*/
__mpa_word_to_hexstr(digits, n->d[d_idx], caseing);
/* find the left-most non-zero digit */
i = NIBBLES_PER_WORD;
while (i-- > 0)
if (digits[i] != '0')
break;
while (i >= 0)
*str++ = digits[i--];
/* convert each word to a hex string */
d_idx--;
while (d_idx >= 0) {
__mpa_word_to_hexstr(digits, n->d[d_idx], caseing);
i = NIBBLES_PER_WORD - 1;
while (i >= 0)
*str++ = digits[i--];
d_idx--;
}
hex_digits = (int)(str - cptr);
*str++ = '\0';
return hex_digits;
}
/* --------------------------------------------------------------------
* Function: __mpa_count_leading_zero_bits
*
*
*/
static mpa_word_t __mpa_count_leading_zero_bits(mpa_word_t w)
{
mpa_word_t mask;
mpa_word_t zeros;
if (w == 0)
return MPA_WORD_SIZE;
mask = ((mpa_word_t)1 << (MPA_WORD_SIZE - 1));
zeros = 0;
while (!(w & mask)) {
zeros++;
mask >>= 1;
}
return zeros;
}
/* --------------------------------------------------------------------
* Function: mpa_SizeInBase
*
* Returns the number of characters needed to print |n| in base 255.
*/
static mpa_word_t __mpa_size_in_base_255(const mpanum n)
{
mpa_word_t totalbits;
/* number of leading zero bits in the msw of n */
mpa_word_t zerobits_msw;
if (__mpanum_is_zero(n))
return 1;
zerobits_msw = __mpa_count_leading_zero_bits(
n->d[__mpanum_size(n) - 1]);
totalbits = WORD_SIZE * __mpanum_size(n) - zerobits_msw;
return (totalbits + 7) / 8;
}
/* --------------------------------------------------------------------
* Function: mpa_get_str_size
*
* Return the max size of the string representing a Big Number
*/
int mpa_get_str_size(void)
{
return MPA_STR_MAX_SIZE;
}
/*************************************************************
*
* LIB FUNCTIONS
*
*************************************************************/
/* --------------------------------------------------------------------
* Function: mpa_set_str
*
* Assigns dest the value of the digitstr, where digitstr is a character
* string.
* If the digitstr starts with a valid number, the valid part will be
* converted and the rest of the digitstr will not be parsed further.
* digitstr is assumed to be in base 16.
* Returns -1 if the digitstr was malformed, and the number of base digits
* converted (not including leading zeros) if the conversion was OK.
* If the digitstr is a null-ptr we return -1.
* If the digitstr is empty, we don't touch dest and just returns 0.
* If the digitstr only consists of white spaces, we set dest to zero
* returns 0.
*/
int mpa_set_str(mpanum dest, const char *digitstr)
{
/* length of digitstr after removal of base indicator and spaces */
int dlen;
int negative; /* ==1 if number is negative, 0 otherwise */
int c; /* value of characters in digitstr */
/* a buffer holding the integer values of the digits */
static unsigned char buf[MPA_STR_MAX_SIZE];
/* number of digits in digitstr which has been place in buf */
int bufidx;
const char *endp; /* points to the end of digitstr */
int retval;
/*
* Pointer intto dest->d where we should put the next word during
* conversion.
*/
mpa_word_t *w;
int i; /* loop variable */
/* some basic sanity checks first */
if (*digitstr == 0)
return 0;
/* remove leading spaces */
do {
c = (unsigned char)*digitstr++;
} while (__mpa_isspace(c));
/* check negative sign */
negative = 0;
if (c == '-') {
negative = 1;
c = (unsigned char)*digitstr++;
}
if (c == '\0') {
mpa_set_word(dest, 0);
return 0;
}
/* see if we have a '0x' prefix */
if (c == '0') {
c = (unsigned char)*digitstr++;
if (c == 'x' || c == 'X')
c = (unsigned char)*digitstr++;
}
/* skip leading zeros and spaces */
while (c == '0' || __mpa_isspace(c))
c = (unsigned char)*digitstr++;
/* check if we had a simple "0" string */
if (c == '\0') {
mpa_set_word(dest, 0);
return 0;
}
/* find the end of digitstr */
endp = digitstr;
while (*endp != 0)
endp++;
/* + 1 since we have one character in 'c' */
dlen = (int)(endp - digitstr) + 1;
if (dlen > MPA_STR_MAX_SIZE) {
EMSG("data len (%d) > max size (%d)", dlen, MPA_STR_MAX_SIZE);
retval = -1;
goto cleanup;
}
/* convert to a buffer of bytes */
bufidx = 0;
while (__mpa_is_char_in_base(16, c)) {
if (!__mpa_isspace(c))
buf[bufidx++] = __mpa_digit_value(c);
c = (unsigned char)*digitstr++;
}
if (bufidx == 0) {
retval = -1;
goto cleanup;
}
if (__mpa_digitstr_to_binary_wsize_base_16(bufidx) >
__mpanum_alloced(dest)) {
EMSG("binary buffer (%d) > alloced buffer (%d)",
__mpa_digitstr_to_binary_wsize_base_16(bufidx),
__mpanum_alloced(dest));
retval = -1;
goto cleanup;
}
retval = bufidx;
w = dest->d;
mpa_set_word(dest, 0);
/* start converting */
*w = 0;
i = BYTES_PER_WORD;
dest->size = 1;
bufidx--; /* dec to get inside buf range */
while (bufidx > 1) {
*w ^= ((((mpa_word_t)buf[bufidx - 1] << 4) ^ (buf[bufidx])) <<
((mpa_word_t)(BYTES_PER_WORD - i) << 3));
i--;
bufidx -= 2;
if (i == 0) {
w++;
*w = 0;
i = BYTES_PER_WORD;
dest->size++;
}
}
if (bufidx == 1)
*w ^= ((((mpa_word_t)buf[bufidx - 1] << 4) ^ (buf[bufidx])) <<
((mpa_word_t)(BYTES_PER_WORD - i) << 3));
if (bufidx == 0)
*w ^= ((mpa_word_t)buf[bufidx] <<
((mpa_word_t)(BYTES_PER_WORD - i) << 3));
if (negative)
__mpanum_neg(dest);
cleanup:
return retval;
}
/* --------------------------------------------------------------------
* Function: mpa_get_str
*
* Prints a representation of n into str.
* The length allocated is the space needed to print n plus additional
* chars for the minus sign and the terminating '\0' char.
* A pointer to str is returned. If something went wrong, we return 0.
*
* mode is one of the following:
* MPA_STRING_MODE_HEX_UC hex notation using upper case
* MPA_STRING_MODE_HEX_LC hex notation using lower case
*
*/
char *mpa_get_str(char *str, int mode, const mpanum n)
{
char *s = str;
assert(str);
/* insert a minus sign */
if (__mpanum_sign(n) == MPA_NEG_SIGN) {
*s = '-';
s++;
}
switch (mode) {
case MPA_STRING_MODE_HEX_UC:
__mpa_mpanum_to_hexstr(s, 0, n);
break;
case MPA_STRING_MODE_HEX_LC:
__mpa_mpanum_to_hexstr(s, 1, n);
break;
default:
return 0;
}
return str;
}
#endif /* #if defined (MPA_INCLUDE_STRING_CONVERSION) */
static mpa_word_t set_word(const uint8_t *in, size_t in_len)
{
int i;
mpa_word_t out;
out = 0;
for (i = in_len - 1; i >= 0; i--)
out |= (mpa_word_t)in[i] << ((in_len - i - 1) * 8);
return out;
}
int mpa_set_oct_str(mpanum dest, const uint8_t *buffer, size_t buffer_len,
bool negative)
{
const uint8_t *buf = buffer;
int bufidx = buffer_len;
mpa_word_t *w;
/* Strip of leading zero octets */
while (bufidx > 0) {
if (*buf != 0)
break;
bufidx--;
buf++;
}
if (bufidx == 0) {
mpa_set_word(dest, 0);
return 0;
}
/*
* bufidx is now indexing one byte past past the last byte in the octet
* string relative to buf.
*/
if ((size_t) (bufidx - 1) > (BYTES_PER_WORD * __mpanum_alloced(dest)))
return -1; /* No space */
w = dest->d;
mpa_set_word(dest, 0);
/* start converting */
dest->size = 0;
while (bufidx > 0) {
int l = __MIN(BYTES_PER_WORD, bufidx);
bufidx -= l;
*w = set_word(buf + bufidx, l);
w++;
dest->size++;
}
if (negative)
__mpanum_neg(dest);
return 0;
}
static void get_word(mpa_word_t in, uint8_t out[BYTES_PER_WORD])
{
int i;
for (i = BYTES_PER_WORD - 1; i >= 0; i--) {
out[i] = in & UINT8_MAX;
in >>= 8;
}
}
int mpa_get_oct_str(uint8_t *buffer, size_t *buffer_len, const mpanum n)
{
size_t req_blen = __mpa_size_in_base_255(n);
uint8_t first_word[BYTES_PER_WORD];
size_t bufidx = 0;
int d_idx;
int i;
if (*buffer_len < req_blen) {
*buffer_len = req_blen;
return -1;
}
/* get high word with data in, watch out for zero case */
d_idx = __mpanum_size(n);
if (d_idx == 0) {
memset(buffer, 0, *buffer_len);
goto out;
}
d_idx--;
/* Strip of leading zero octets */
get_word(n->d[d_idx], first_word);
for (i = 0; i < BYTES_PER_WORD; i++) {
if (first_word[i] != 0) {
memcpy(buffer, first_word + i, BYTES_PER_WORD - i);
bufidx = BYTES_PER_WORD - i;
break;
}
}
d_idx--;
while (d_idx >= 0) {
if (bufidx > req_blen)
return -1;
get_word(n->d[d_idx], buffer + bufidx);
bufidx += BYTES_PER_WORD;
d_idx--;
}
out:
*buffer_len = req_blen;
return 0;
}
/* end of file mpa_io.c */