aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp')
-rw-r--r--src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp566
1 files changed, 566 insertions, 0 deletions
diff --git a/src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp b/src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp
new file mode 100644
index 0000000000..a312ba3a1c
--- /dev/null
+++ b/src/libs/3rdparty/botan/src/lib/math/numbertheory/numthry.cpp
@@ -0,0 +1,566 @@
+/*
+* Number Theory Functions
+* (C) 1999-2011,2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/pow_mod.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/rng.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/monty_exp.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Return the number of 0 bits at the end of n
+*/
+size_t low_zero_bits(const BigInt& n)
+ {
+ size_t low_zero = 0;
+
+ if(n.is_positive() && n.is_nonzero())
+ {
+ for(size_t i = 0; i != n.size(); ++i)
+ {
+ const word x = n.word_at(i);
+
+ if(x)
+ {
+ low_zero += ctz(x);
+ break;
+ }
+ else
+ low_zero += BOTAN_MP_WORD_BITS;
+ }
+ }
+
+ return low_zero;
+ }
+
+/*
+* Calculate the GCD
+*/
+BigInt gcd(const BigInt& a, const BigInt& b)
+ {
+ if(a.is_zero() || b.is_zero())
+ return 0;
+ if(a == 1 || b == 1)
+ return 1;
+
+ BigInt X[2] = { a, b };
+ X[0].set_sign(BigInt::Positive);
+ X[1].set_sign(BigInt::Positive);
+
+ const size_t shift = std::min(low_zero_bits(X[0]), low_zero_bits(X[1]));
+
+ X[0] >>= shift;
+ X[1] >>= shift;
+
+ while(X[0].is_nonzero())
+ {
+ X[0] >>= low_zero_bits(X[0]);
+ X[1] >>= low_zero_bits(X[1]);
+
+ const uint8_t sel = static_cast<uint8_t>(X[0] >= X[1]);
+
+ X[sel^1] -= X[sel];
+ X[sel^1] >>= 1;
+ }
+
+ return (X[1] << shift);
+ }
+
+/*
+* Calculate the LCM
+*/
+BigInt lcm(const BigInt& a, const BigInt& b)
+ {
+ return ((a * b) / gcd(a, b));
+ }
+
+/*
+Sets result to a^-1 * 2^k mod a
+with n <= k <= 2n
+Returns k
+
+"The Montgomery Modular Inverse - Revisited" Çetin Koç, E. Savas
+https://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.75.8377
+
+A const time implementation of this algorithm is described in
+"Constant Time Modular Inversion" Joppe W. Bos
+http://www.joppebos.com/files/CTInversion.pdf
+*/
+size_t almost_montgomery_inverse(BigInt& result,
+ const BigInt& a,
+ const BigInt& p)
+ {
+ size_t k = 0;
+
+ BigInt u = p, v = a, r = 0, s = 1;
+
+ while(v > 0)
+ {
+ if(u.is_even())
+ {
+ u >>= 1;
+ s <<= 1;
+ }
+ else if(v.is_even())
+ {
+ v >>= 1;
+ r <<= 1;
+ }
+ else if(u > v)
+ {
+ u -= v;
+ u >>= 1;
+ r += s;
+ s <<= 1;
+ }
+ else
+ {
+ v -= u;
+ v >>= 1;
+ s += r;
+ r <<= 1;
+ }
+
+ ++k;
+ }
+
+ if(r >= p)
+ {
+ r -= p;
+ }
+
+ result = p - r;
+
+ return k;
+ }
+
+BigInt normalized_montgomery_inverse(const BigInt& a, const BigInt& p)
+ {
+ BigInt r;
+ size_t k = almost_montgomery_inverse(r, a, p);
+
+ for(size_t i = 0; i != k; ++i)
+ {
+ if(r.is_odd())
+ r += p;
+ r >>= 1;
+ }
+
+ return r;
+ }
+
+BigInt ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
+ {
+ if(n.is_negative() || mod.is_negative())
+ throw Invalid_Argument("ct_inverse_mod_odd_modulus: arguments must be non-negative");
+ if(mod < 3 || mod.is_even())
+ throw Invalid_Argument("Bad modulus to ct_inverse_mod_odd_modulus");
+ if(n >= mod)
+ throw Invalid_Argument("ct_inverse_mod_odd_modulus n >= mod not supported");
+
+ /*
+ This uses a modular inversion algorithm designed by Niels Möller
+ and implemented in Nettle. The same algorithm was later also
+ adapted to GMP in mpn_sec_invert.
+
+ It can be easily implemented in a way that does not depend on
+ secret branches or memory lookups, providing resistance against
+ some forms of side channel attack.
+
+ There is also a description of the algorithm in Appendix 5 of "Fast
+ Software Polynomial Multiplication on ARM Processors using the NEON Engine"
+ by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
+ Dahab in LNCS 8182
+ https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf
+
+ Thanks to Niels for creating the algorithm, explaining some things
+ about it, and the reference to the paper.
+ */
+
+ // todo allow this to be pre-calculated and passed in as arg
+ BigInt mp1o2 = (mod + 1) >> 1;
+
+ const size_t mod_words = mod.sig_words();
+ BOTAN_ASSERT(mod_words > 0, "Not empty");
+
+ BigInt a = n;
+ BigInt b = mod;
+ BigInt u = 1, v = 0;
+
+ a.grow_to(mod_words);
+ u.grow_to(mod_words);
+ v.grow_to(mod_words);
+ mp1o2.grow_to(mod_words);
+
+ secure_vector<word>& a_w = a.get_word_vector();
+ secure_vector<word>& b_w = b.get_word_vector();
+ secure_vector<word>& u_w = u.get_word_vector();
+ secure_vector<word>& v_w = v.get_word_vector();
+
+ CT::poison(a_w.data(), a_w.size());
+ CT::poison(b_w.data(), b_w.size());
+ CT::poison(u_w.data(), u_w.size());
+ CT::poison(v_w.data(), v_w.size());
+
+ // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
+ size_t bits = 2 * mod.bits();
+
+ while(bits--)
+ {
+ /*
+ const word odd = a.is_odd();
+ a -= odd * b;
+ const word underflow = a.is_negative();
+ b += a * underflow;
+ a.set_sign(BigInt::Positive);
+
+ a >>= 1;
+
+ if(underflow)
+ {
+ std::swap(u, v);
+ }
+
+ u -= odd * v;
+ u += u.is_negative() * mod;
+
+ const word odd_u = u.is_odd();
+
+ u >>= 1;
+ u += mp1o2 * odd_u;
+ */
+
+ const word odd_a = a_w[0] & 1;
+
+ //if(odd_a) a -= b
+ word underflow = bigint_cnd_sub(odd_a, a_w.data(), b_w.data(), mod_words);
+
+ //if(underflow) { b -= a; a = abs(a); swap(u, v); }
+ bigint_cnd_add(underflow, b_w.data(), a_w.data(), mod_words);
+ bigint_cnd_abs(underflow, a_w.data(), mod_words);
+ bigint_cnd_swap(underflow, u_w.data(), v_w.data(), mod_words);
+
+ // a >>= 1
+ bigint_shr1(a_w.data(), mod_words, 0, 1);
+
+ //if(odd_a) u -= v;
+ word borrow = bigint_cnd_sub(odd_a, u_w.data(), v_w.data(), mod_words);
+
+ // if(borrow) u += p
+ bigint_cnd_add(borrow, u_w.data(), mod.data(), mod_words);
+
+ const word odd_u = u_w[0] & 1;
+
+ // u >>= 1
+ bigint_shr1(u_w.data(), mod_words, 0, 1);
+
+ //if(odd_u) u += mp1o2;
+ bigint_cnd_add(odd_u, u_w.data(), mp1o2.data(), mod_words);
+ }
+
+ CT::unpoison(a_w.data(), a_w.size());
+ CT::unpoison(b_w.data(), b_w.size());
+ CT::unpoison(u_w.data(), u_w.size());
+ CT::unpoison(v_w.data(), v_w.size());
+
+ BOTAN_ASSERT(a.is_zero(), "A is zero");
+
+ if(b != 1)
+ return 0;
+
+ return v;
+ }
+
+/*
+* Find the Modular Inverse
+*/
+BigInt inverse_mod(const BigInt& n, const BigInt& mod)
+ {
+ if(mod.is_zero())
+ throw BigInt::DivideByZero();
+ if(mod.is_negative() || n.is_negative())
+ throw Invalid_Argument("inverse_mod: arguments must be non-negative");
+
+ if(n.is_zero() || (n.is_even() && mod.is_even()))
+ return 0; // fast fail checks
+
+ if(mod.is_odd() && n < mod)
+ return ct_inverse_mod_odd_modulus(n, mod);
+
+ return inverse_euclid(n, mod);
+ }
+
+BigInt inverse_euclid(const BigInt& n, const BigInt& mod)
+ {
+ if(mod.is_zero())
+ throw BigInt::DivideByZero();
+ if(mod.is_negative() || n.is_negative())
+ throw Invalid_Argument("inverse_mod: arguments must be non-negative");
+
+ if(n.is_zero() || (n.is_even() && mod.is_even()))
+ return 0; // fast fail checks
+
+ BigInt u = mod, v = n;
+ BigInt A = 1, B = 0, C = 0, D = 1;
+
+ while(u.is_nonzero())
+ {
+ const size_t u_zero_bits = low_zero_bits(u);
+ u >>= u_zero_bits;
+ for(size_t i = 0; i != u_zero_bits; ++i)
+ {
+ if(A.is_odd() || B.is_odd())
+ { A += n; B -= mod; }
+ A >>= 1; B >>= 1;
+ }
+
+ const size_t v_zero_bits = low_zero_bits(v);
+ v >>= v_zero_bits;
+ for(size_t i = 0; i != v_zero_bits; ++i)
+ {
+ if(C.is_odd() || D.is_odd())
+ { C += n; D -= mod; }
+ C >>= 1; D >>= 1;
+ }
+
+ if(u >= v) { u -= v; A -= C; B -= D; }
+ else { v -= u; C -= A; D -= B; }
+ }
+
+ if(v != 1)
+ return 0; // no modular inverse
+
+ while(D.is_negative()) D += mod;
+ while(D >= mod) D -= mod;
+
+ return D;
+ }
+
+word monty_inverse(word input)
+ {
+ if(input == 0)
+ throw Exception("monty_inverse: divide by zero");
+
+ word b = input;
+ word x2 = 1, x1 = 0, y2 = 0, y1 = 1;
+
+ // First iteration, a = n+1
+ word q = bigint_divop(1, 0, b);
+ word r = (MP_WORD_MAX - q*b) + 1;
+ word x = x2 - q*x1;
+ word y = y2 - q*y1;
+
+ word a = b;
+ b = r;
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+
+ while(b > 0)
+ {
+ q = a / b;
+ r = a - q*b;
+ x = x2 - q*x1;
+ y = y2 - q*y1;
+
+ a = b;
+ b = r;
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+
+ const word check = y2 * input;
+ BOTAN_ASSERT_EQUAL(check, 1, "monty_inverse result is inverse of input");
+
+ // Now invert in addition space
+ y2 = (MP_WORD_MAX - y2) + 1;
+
+ return y2;
+ }
+
+/*
+* Modular Exponentiation
+*/
+BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod)
+ {
+ if(mod.is_negative() || mod == 1)
+ {
+ return 0;
+ }
+
+ if(base.is_zero() || mod.is_zero())
+ {
+ if(exp.is_zero())
+ return 1;
+ return 0;
+ }
+
+ Power_Mod pow_mod(mod);
+
+ /*
+ * Calling set_base before set_exponent means we end up using a
+ * minimal window. This makes sense given that here we know that any
+ * precomputation is wasted.
+ */
+
+ if(base.is_negative())
+ {
+ pow_mod.set_base(-base);
+ pow_mod.set_exponent(exp);
+ if(exp.is_even())
+ return pow_mod.execute();
+ else
+ return (mod - pow_mod.execute());
+ }
+ else
+ {
+ pow_mod.set_base(base);
+ pow_mod.set_exponent(exp);
+ return pow_mod.execute();
+ }
+ }
+
+namespace {
+
+bool mr_witness(BigInt&& y,
+ const Modular_Reducer& reducer_n,
+ const BigInt& n_minus_1, size_t s)
+ {
+ if(y == 1 || y == n_minus_1)
+ return false;
+
+ for(size_t i = 1; i != s; ++i)
+ {
+ y = reducer_n.square(y);
+
+ if(y == 1) // found a non-trivial square root
+ return true;
+
+ /*
+ -1 is the trivial square root of unity, so ``a`` is not a
+ witness for this number - give up
+ */
+ if(y == n_minus_1)
+ return false;
+ }
+
+ return true; // is a witness
+ }
+
+size_t mr_test_iterations(size_t n_bits, size_t prob, bool random)
+ {
+ const size_t base = (prob + 2) / 2; // worst case 4^-t error rate
+
+ /*
+ * If the candidate prime was maliciously constructed, we can't rely
+ * on arguments based on p being random.
+ */
+ if(random == false)
+ return base;
+
+ /*
+ * For randomly chosen numbers we can use the estimates from
+ * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
+ *
+ * These values are derived from the inequality for p(k,t) given on
+ * the second page.
+ */
+ if(prob <= 128)
+ {
+ if(n_bits >= 1536)
+ return 4; // < 2^-133
+ if(n_bits >= 1024)
+ return 6; // < 2^-133
+ if(n_bits >= 512)
+ return 12; // < 2^-129
+ if(n_bits >= 256)
+ return 29; // < 2^-128
+ }
+
+ /*
+ If the user desires a smaller error probability than we have
+ precomputed error estimates for, just fall back to using the worst
+ case error rate.
+ */
+ return base;
+ }
+
+}
+
+/*
+* Test for primality using Miller-Rabin
+*/
+bool is_prime(const BigInt& n, RandomNumberGenerator& rng,
+ size_t prob, bool is_random)
+ {
+ if(n == 2)
+ return true;
+ if(n <= 1 || n.is_even())
+ return false;
+
+ // Fast path testing for small numbers (<= 65521)
+ if(n <= PRIMES[PRIME_TABLE_SIZE-1])
+ {
+ const uint16_t num = static_cast<uint16_t>(n.word_at(0));
+
+ return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
+ }
+
+ const size_t test_iterations =
+ mr_test_iterations(n.bits(), prob, is_random && rng.is_seeded());
+
+ const BigInt n_minus_1 = n - 1;
+ const size_t s = low_zero_bits(n_minus_1);
+ const BigInt nm1_s = n_minus_1 >> s;
+ const size_t n_bits = n.bits();
+
+ const Modular_Reducer mod_n(n);
+ auto monty_n = std::make_shared<Montgomery_Params>(n, mod_n);
+
+ const size_t powm_window = 4;
+
+ for(size_t i = 0; i != test_iterations; ++i)
+ {
+ BigInt a;
+
+ if(rng.is_seeded())
+ {
+ a = BigInt::random_integer(rng, 2, n_minus_1);
+ }
+ else
+ {
+ /*
+ * If passed a null RNG just use 2,3,5, ... as bases
+ *
+ * This is not ideal but in certain circumstances we need to
+ * test for primality but have no RNG available.
+ */
+ a = PRIMES[i];
+ }
+
+ auto powm_a_n = monty_precompute(monty_n, a, powm_window);
+
+ BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits);
+
+ if(mr_witness(std::move(y), mod_n, n_minus_1, s))
+ return false;
+ }
+
+ return true;
+ }
+
+}