// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2014, STMicroelectronics International N.V. */ #include #include #include /* * 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 */