/****************************************************************************** * Copyright (c) 2000-2021 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Balasko, Jeno * Bibo, Zoltan * Forstner, Matyas * Gecse, Roland * Koppany, Csaba * Kovacs, Ferenc * Raduly, Csaba * Szabo, Janos Zoltan – initial implementation * ******************************************************************************/ #include "../common/dbgnew.hh" #include "Int.hh" #include "string.hh" #include "error.h" #include "Setting.hh" #include <openssl/crypto.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> // We cannot check without using a "./configure" script or such if we have // llabs() or not. Define our own function instead. inline long long ll_abs(long long x) { return ((x >= 0) ? (x) : (-x)); } namespace Common { string Int2string(const Int& i) { char *s = NULL; s = mprintf("%lld", i); string str(s); Free(s); return str; } Int string2Int(const char *s, const Location& loc) { errno = 0; Int i = strtoll(s, NULL, 10); switch (errno) { case ERANGE: { if (loc.get_filename() != NULL) { loc.error("Overflow when converting `%s' to integer value: %s", s, strerror(errno)); } else { FATAL_ERROR("Overflow when converting `%s' to integer value: %s", s, strerror(errno)); } break; } case 0: break; default: FATAL_ERROR("Unexpected error when converting `%s' to integer: %s", s, strerror(errno)); } return i; } int_val_t::int_val_t() : native_flag(true) { val.openssl = NULL; } int_val_t::int_val_t(const int_val_t& v) { native_flag = v.is_native(); if (native_flag) val.native = v.get_val(); else val.openssl = BN_dup(v.get_val_openssl()); } int_val_t::int_val_t(const char *s, const Location& loc) { BIGNUM *n = NULL; if (!BN_dec2bn(&n, *s == '+' ? s + 1 : s)) loc.error("Unexpected error when converting `%s' to integer", s); if (BN_num_bits(n) > (int)sizeof(long long) * 8 - 1) { native_flag = false; val.openssl = n; } else { native_flag = true; val.native = string2Int(s, loc); BN_free(n); } } int_val_t::~int_val_t() { if (!native_flag) BN_free(val.openssl); } string int_val_t::t_str() const { char *tmp = NULL; if (native_flag) { tmp = mprintf("%lld", val.native); string s(tmp); Free(tmp); return s; } else { if (!(tmp = BN_bn2dec(val.openssl))) FATAL_ERROR("int_val_t::t_str()"); string s(tmp); OPENSSL_free(tmp); return s; } } BIGNUM *int_val_t::to_openssl() const { BIGNUM *big = NULL; if (native_flag) { string str = Int2string(val.native); if (!BN_dec2bn(&big, str.c_str())) FATAL_ERROR("int_val_t::to_openssl()"); } else { big = BN_dup(val.openssl); if (!big) FATAL_ERROR("int_val_t::to_openssl()"); } return big; } bool int_val_t::operator==(Int right) const { if (!native_flag) return false; return val.native == right; } bool int_val_t::operator==(const int_val_t& v) const { if (native_flag != v.is_native()) return false; if (native_flag) return val.native == v.get_val(); return !BN_cmp(val.openssl, v.get_val_openssl()); } bool int_val_t::operator<(const int_val_t& v) const { if (native_flag) { if (v.is_native()) { return val.native < v.get_val(); } else { BIGNUM *this_big = to_openssl(); if (!this_big) FATAL_ERROR("int_val_t::operator<(int_val_t& v)"); int this_equ = BN_cmp(this_big, v.get_val_openssl()); BN_free(this_big); return this_equ == -1; } } else { if (v.is_native()) { BIGNUM *v_big = v.to_openssl(); if (!v_big) FATAL_ERROR("int_val_t::operator<(int_val_t& v)"); int v_equ = BN_cmp(val.openssl, v_big); BN_free(v_big); return v_equ == -1; } else { return BN_cmp(val.openssl, v.val.openssl) == -1; } } } int_val_t int_val_t::operator-() const { if (native_flag) { if (val.native == LLONG_MIN) { BIGNUM *result = int_val_t(LLONG_MIN).to_openssl(); BN_set_negative(result, 0); return int_val_t(result); } else { return int_val_t(-val.native); } } else { BIGNUM *llong_max_plus_one = int_val_t(LLONG_MIN).to_openssl(); BN_set_negative(llong_max_plus_one, 0); int cmp = BN_cmp(val.openssl, llong_max_plus_one); BN_free(llong_max_plus_one); if (cmp == 0) { return int_val_t(LLONG_MIN); } else { BIGNUM *result = BN_dup(val.openssl); BN_set_negative(result, !BN_is_negative(result)); return int_val_t(result); } } } int_val_t int_val_t::operator+(const int_val_t& right) const { // a + b = a add b // a + -b = a sub b // -a + b = b sub a // -a + -b = -(a add b) // Use only inline functions and BN_* directly. Call out for operator- in // the beginning. bool a_neg = is_negative(); bool b_neg = right.is_negative(); if (!a_neg && b_neg) return operator-(-right); if (a_neg && !b_neg) return right.operator-(-(*this)); if (native_flag) { if (right.is_native()) { unsigned long long result = val.native + right.get_val(); long long result_ = val.native + right.get_val(); bool r_neg = a_neg && b_neg; if (static_cast<long long>(result) != result_ || (!r_neg && result_ < 0) || (r_neg && result_ > 0)) { // We can safely assume that the sum of two non-negative long long // values fit in an unsigned long long. limits.h says: // # ifndef ULLONG_MAX // # define ULLONG_MAX (LLONG_MAX * 2ULL + 1) // # endif // This is the most complicated case. We cannot be sure that // sizeof(BN_ULONG) == sizeof(long long). First convert the long long // to string and feed BN_dec2bn. BIGNUM *left_ = to_openssl(); BIGNUM *right_ = right.to_openssl(); BN_add(left_, left_, right_); BN_free(right_); return int_val_t(left_); } else { return int_val_t(result_); } } else { // long long (>= 0) + BIGNUM == BIGNUM. BIGNUM *result = BN_new(); BIGNUM *left_ = to_openssl(); BN_add(result, left_, right.get_val_openssl()); return int_val_t(result); } } else { // BIGNUM + long long (>= 0) == BIGNUM. BIGNUM *result = BN_new(); BIGNUM *right_; right_ = right.is_native() ? right.to_openssl() : right.get_val_openssl(); BN_add(result, val.openssl, right_); if (right.is_native()) BN_free(right_); return int_val_t(result); } } int_val_t int_val_t::operator-(const int_val_t& right) const { // a - b = a sub b // -a - b = -(a add b) // a - -b = a add b // -a - -b = -a add b = b sub a bool a_neg = is_negative(); bool b_neg = right.is_negative(); if (!a_neg && b_neg) return operator+(-right); if (a_neg && !b_neg) return -right.operator+(-(*this)); if (native_flag) { if (right.is_native()) { // Since both operands are non-negative the most negative result of a // subtraction can be -LLONG_MAX and according to limits.h: // # ifndef LLONG_MIN // # define LLONG_MIN (-LLONG_MAX-1) // # endif return int_val_t(val.native - right.get_val()); } else { BIGNUM *left_bn = to_openssl(); BN_sub(left_bn, left_bn, right.get_val_openssl()); // The result can be small enough to fit in long long. The same is true // for division. Back conversion is a really costly operation using // strings all the time. TODO Improve it. if (BN_num_bits(left_bn) <= (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(left_bn); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); BN_free(left_bn); return int_val_t(result_ll); } else { return int_val_t(left_bn); } } } else { BIGNUM *result = BN_new(); BIGNUM *right_bn; right_bn = right.is_native() ? right.to_openssl() : right.get_val_openssl(); BN_sub(result, val.openssl, right_bn); if (right.is_native()) BN_free(right_bn); if (BN_num_bits(result) <= (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(result); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); return int_val_t(result_ll); } else { return int_val_t(result); } } } int_val_t int_val_t::operator*(const int_val_t& right) const { if ((native_flag && val.native == 0LL) || (right.native_flag && right.val.native == 0LL)) return int_val_t(0LL); if (native_flag) { if (right.native_flag) { // 2^15 is used as a simple heuristic. // TODO: Improve. if (ll_abs(val.native) < 32768LL && ll_abs(right.val.native) < 32768LL) { return int_val_t(val.native * right.val.native); } else { BIGNUM *left_bn = to_openssl(); BIGNUM *right_bn = right.to_openssl(); BN_CTX *ctx = BN_CTX_new(); BN_mul(left_bn, left_bn, right_bn, ctx); BN_CTX_free(ctx); BN_free(right_bn); if (BN_num_bits(left_bn) < (int)sizeof(long long) * 8) { BN_free(left_bn); return int_val_t(val.native * right.val.native); } else { return int_val_t(left_bn); } } } else { BIGNUM *this_bn = to_openssl(); BN_CTX *ctx = BN_CTX_new(); BN_mul(this_bn, this_bn, right.get_val_openssl(), ctx); BN_CTX_free(ctx); return int_val_t(this_bn); } } else { BIGNUM *result = BN_new(); BIGNUM *right_bn; BN_CTX *ctx = BN_CTX_new(); right_bn = right.native_flag ? right.to_openssl() : right.get_val_openssl(); BN_mul(result, val.openssl, right_bn, ctx); BN_CTX_free(ctx); if (right.native_flag) BN_free(right_bn); return int_val_t(result); } } int_val_t int_val_t::operator/(const int_val_t& right) const { if (native_flag && val.native == 0LL) return int_val_t(0LL); if (right.is_native() && right.get_val() == 0LL) FATAL_ERROR("Division by zero after semantic check"); if (native_flag) { if (right.native_flag) { return int_val_t(val.native / right.get_val()); } else { BIGNUM *left_bn = to_openssl(); BN_CTX *ctx = BN_CTX_new(); BN_div(left_bn, NULL, left_bn, right.get_val_openssl(), ctx); BN_CTX_free(ctx); if (BN_num_bits(left_bn) <= (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(left_bn); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); BN_free(left_bn); return int_val_t(result_ll); } else { return int_val_t(left_bn); } } } else { BIGNUM *result = BN_new(); BIGNUM *right_bn; BN_CTX *ctx = BN_CTX_new(); right_bn = right.is_native() ? right.to_openssl() : right.get_val_openssl(); BN_div(result, NULL, val.openssl, right_bn, ctx); BN_CTX_free(ctx); if (BN_num_bits(result) <= (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(result); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); return int_val_t(result_ll); } else { if (right.is_native()) BN_free(right_bn); return int_val_t(result); } } } int_val_t int_val_t::operator&(Int right) const { // TODO Right can be int_val_t. Now it works only if right fits in // BN_ULONG. If it's not true right must be converted to BIGNUM and the // bits should be set with BN_is_bit_set/BN_set_bit. BN_ULONG right_bn_ulong = (BN_ULONG)right; if (right != (long long)right_bn_ulong) FATAL_ERROR("Bitmask is too big"); if (native_flag) { return int_val_t(val.native & right); } else { BIGNUM *tmp = BN_dup(val.openssl); BN_mask_bits(tmp, sizeof(BN_ULONG) * 8); BN_ULONG word = BN_get_word(tmp); BN_free(tmp); return int_val_t(word & right_bn_ulong); } } int_val_t int_val_t::operator>>(Int right) const { if (native_flag) { // Shifting right (or left) with a number greater or equal to the bits of // the type of the left operand has an undefined behaviour. // http://bytes.com/groups/c/495137-right-shift-weird-result-why Int shifted_value = right >= static_cast<Int>(sizeof(Int) * 8) ? 0 : val.native >> right; return int_val_t(shifted_value); } else { BIGNUM *result = BN_new(); BN_rshift(result, val.openssl, right); if (BN_num_bits(result) < (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(result); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); BN_free(result); return int_val_t(result_ll); } else { return int_val_t(result); } } } const Int& int_val_t::get_val() const { if (!native_flag) FATAL_ERROR("Invalid conversion of a large integer value"); return val.native; } BIGNUM *int_val_t::get_val_openssl() const { if (native_flag) FATAL_ERROR("Invalid conversion of a large integer value"); return val.openssl; } Real int_val_t::to_real() const { if (native_flag) { return (double)val.native; } else { char *result_str = BN_bn2dec(val.openssl); Real result = 0.0; // Use fixed-point notation. The mantissa is usually at most 52-bits. // Bigger integer values will be rounded. if (sscanf(result_str, "%lf", &result) != 1) FATAL_ERROR("Conversion of integer value `%s' to float failed", result_str); // No deallocation, it'll crash anyway... OPENSSL_free(result_str); return result; } } int_val_t& int_val_t::operator=(const int_val_t& right) { if (!native_flag) BN_free(val.openssl); native_flag = right.native_flag; if (native_flag) val.native = right.get_val(); else val.openssl = BN_dup(right.get_val_openssl()); return *this; } int_val_t& int_val_t::operator<<=(Int right) { // It makes no sense to support negative operands. GCC returns constant "0" // with "warning: left shift count is negative" for these shifts. // BN_set_word is not enough since sizeof(BN_ULONG) != sizeof(long long). // In TTCN-3 <<= right means >>= -right. if (right < 0) FATAL_ERROR("The second operand of bitwise shift operators " "cannot be negative"); if (right == 0) return *this; if (native_flag) { BIGNUM *result = BN_new(); BN_dec2bn(&result, Int2string(val.native).c_str()); BN_lshift(result, result, right); if (BN_num_bits(result) > (int)sizeof(long long) * 8 - 1) { val.openssl = result; native_flag = false; } else { val.native <<= right; BN_free(result); } } else { BN_lshift(val.openssl, val.openssl, right); } return *this; } int_val_t& int_val_t::operator>>=(Int right) { if (right < 0) FATAL_ERROR("The second operand of bitwise shift operators " "cannot be negative"); if (right == 0) return *this; if (native_flag) { val.native >>= right; } else { BN_rshift(val.openssl, val.openssl, right); if (BN_num_bits(val.openssl) <= (int)sizeof(long long) * 8 - 1) { char *result_str = BN_bn2dec(val.openssl); Int result_ll = string2Int(result_str, Location()); OPENSSL_free(result_str); native_flag = true; BN_free(val.openssl); val.native = result_ll; } } return *this; } int_val_t& int_val_t::operator+=(Int right) { // Unfortunately we have to check the sign of the "right" operand and // perform addition or subtraction accordingly. if (right == 0) return *this; bool neg = right < 0; if (native_flag) { BIGNUM *result = BN_new(); BN_set_word(result, (BN_ULONG)val.native); if (neg) BN_sub_word(result, (BN_ULONG)right); else BN_add_word(result, (BN_ULONG)right); if (BN_num_bits(result) > (int)sizeof(long long) * 8 - 1) { val.openssl = result; native_flag = false; } else { val.native += right; BN_free(result); } } else { if (neg) BN_sub_word(val.openssl, (BN_ULONG)right); else BN_add_word(val.openssl, (BN_ULONG)right); if (BN_num_bits(val.openssl) <= (int)sizeof(long long) * 8 - 1) { // TODO BN_ULONG != long long. BN_ULONG tmp = BN_get_word(val.openssl); if (BN_is_negative(val.openssl)) tmp *= -1; BN_free(val.openssl); val.native = tmp; native_flag = true; } } return *this; } } // Common