From e0646cb7ec566cc9d30a01a0f31d034aef214dee Mon Sep 17 00:00:00 2001 From: Nick Thompson Date: Fri, 26 Oct 2018 16:58:30 -0600 Subject: [PATCH] Add template argument to green up build. Remove discrete log as we do not have an overflow-resistant mul_mod in boost. --- doc/modular_arithmetic/discrete_log.qbk | 99 -------------- include/boost/integer/discrete_log.hpp | 171 ------------------------ test/Jamfile.v2 | 1 - test/discrete_log_test.cpp | 125 ----------------- test/extended_euclidean_test.cpp | 2 +- 5 files changed, 1 insertion(+), 397 deletions(-) delete mode 100644 doc/modular_arithmetic/discrete_log.qbk delete mode 100644 include/boost/integer/discrete_log.hpp delete mode 100644 test/discrete_log_test.cpp diff --git a/doc/modular_arithmetic/discrete_log.qbk b/doc/modular_arithmetic/discrete_log.qbk deleted file mode 100644 index d0d21a7..0000000 --- a/doc/modular_arithmetic/discrete_log.qbk +++ /dev/null @@ -1,99 +0,0 @@ -[section:discrete_log Discrete Log] - -[section Introduction] - -The discrete log is the inverse of modular exponentiation. -To wit, if /a/[super /x/] = /b/ mod /p/, then we write /x/ = log[sub a](/b/). -Fast algorithms for modular exponentiation exists, but currently there are no polynomial time algorithms known for the discrete logarithm, -a fact which is the basis for the security of Diffie-Hellman key exchange. - -Despite having exponential complexity in the number of bits, the algorithms for discrete logarithm provided by Boost are still useful, -for there are many uses of the discrete logarithm outside of cryptography which do not require massive inputs. -The algorithms provided by Boost should be acceptable up to roughly 32 bits. - -[endsect] - -[section Synopsis] - - #include - - namespace boost { namespace integer { - - template - boost::optional trial_multiplication_discrete_log(Z base, Z arg, Z p); - - - template - class bsgs_discrete_log - { - public: - bsgs_discrete_log(Z base, Z p); - - boost::optional operator()(Z arg) const; - - }; - }} - -[endsect] - -[section Usage] - - -Boost provides two algorithms for the discrete log: Trial multiplication and the "baby-step giant-step" algorithm. -Basic usage is shown below: - - auto logarithm = trial_multiplication_discrete_log(2, 3, 5); - if (logarithm) - { - std::cout << "log_2(3) mod 5 = " << l.value() << std::endl; - } - - auto log_2 = bsgs_discrete_log(2, 5); - int log = log_2(3).value(); - std::cout << "log_2(3) mod 5 = " << log << std::endl; - - -Of these, trial multiplication is more general, requires [bigo](/p/) time and [bigo](1) storage. -The baby-step giant step algorithm requires [bigo]([radic] p) time and [bigo]([radic] p) storage, -and is slightly less general as the base must be coprime to the the modulus. -Let's illustrate this with a few examples: Suppose we wish to compute log[sub 2](3) mod 4. -Since 2[super /x/] = 3 mod 4 has no solution, the result is undefined. - - boost::optional l = trial_multiplication_discrete_log(2, 3, 4); - if (!l) - { - std::cout << "log_2(3) mod 4 is undefined!\n"; - } - -The baby-step giant-step algorithm is less polite when the base and the modulus are not coprime: - - try - { - auto log_2 = bsgs_discrete_log(2, 4); - } - catch(std::exception const & e) - { - // e.what() is gonna tell you 2 and 4 are not coprime: - std::cout << e.what() << std::endl; - } - - -The baby-step giant-step discrete log will *never* compute a logarithm when the base and modulus are not coprime, -because it relies on the existence of modular multiplicative inverses. -However, discrete logarithms can exist even when the base and modulus share a common divisor greater than 1. -For example, since 2[super 1] = 2 mod 4, log[sub 2](2) = 1. -Trial multiplication successfully recovers this value, and `bsgs_discrete_log` blows up. - - -[endsect] - -[section References] -Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013. -[endsect] -[endsect] -[/ -Copyright 2018 Nick Thompson. -Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at -http://www.boost.org/LICENSE_1_0.txt). -] diff --git a/include/boost/integer/discrete_log.hpp b/include/boost/integer/discrete_log.hpp deleted file mode 100644 index d5f94ce..0000000 --- a/include/boost/integer/discrete_log.hpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * (C) Copyright Nick Thompson 2018. - * Use, modification and distribution are subject to the - * Boost Software License, Version 1.0. (See accompanying file - * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - * - * Two methods of computing the discrete logarithm over the multiplicative group of integers mod p. - */ - -#ifndef BOOST_INTEGER_DISCRETE_LOG_HPP -#define BOOST_INTEGER_DISCRETE_LOG_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { namespace integer { - -// base^^x = a mod p <-> x = log_base(a) mod p -template -boost::optional trial_multiplication_discrete_log(Z base, Z arg, Z modulus) -{ - if (base <= 1) - { - std::ostringstream oss; - oss << "The base b is " << base << ", but must be > 1.\n"; - BOOST_THROW_EXCEPTION(std::domain_error(oss.str())); - } - if (modulus < 3) - { - std::ostringstream oss; - oss << "The modulus must be > 2, but is " << modulus << ".\n"; - BOOST_THROW_EXCEPTION(std::domain_error(oss.str())); - } - if (arg < 1) - { - std::ostringstream oss; - oss << "The argument must be > 0, but is " << arg << ".\n"; - BOOST_THROW_EXCEPTION(std::domain_error(oss.str())); - } - if (base >= modulus || arg >= modulus) - { - if (base >= modulus) - { - std::ostringstream oss; - oss << "Error computing the discrete log: The base " << base - << " is greater than the modulus " << modulus - << ". Are the arguments in the wrong order?"; - BOOST_THROW_EXCEPTION(std::domain_error(oss.str())); - } - if (arg >= modulus) - { - std::ostringstream oss; - oss << "Error computing the discrete log: The argument " << arg - << " is greater than the modulus " << modulus - << ". Are the arguments in the wrong order?"; - BOOST_THROW_EXCEPTION(std::domain_error(oss.str())); - } - } - - if (arg == 1) - { - return 0; - } - Z s = 1; - for (Z i = 1; i < modulus; ++i) - { - s = (s * base) % modulus; - if (s == arg) - { - // Maybe a bit trivial assertion. But still a negligible fraction of the total compute time. - BOOST_ASSERT(arg == boost::multiprecision::powm(base, i, modulus)); - return i; - } - } - return {}; -} - -template -class bsgs_discrete_log -{ -public: - bsgs_discrete_log(Z base, Z modulus) : m_p{modulus}, m_base{base} - { - using std::numeric_limits; - static_assert(numeric_limits::is_integer, - "The baby-step, giant-step discrete log works on integral types.\n"); - - if (base <= 1) - { - BOOST_THROW_EXCEPTION(std::logic_error("The base must be > 1.\n")); - } - if (modulus < 3) - { - BOOST_THROW_EXCEPTION(std::logic_error("The modulus must be > 2.\n")); - } - if (base >= modulus) - { - BOOST_THROW_EXCEPTION(std::logic_error("Error computing the discrete log: Are your arguments in the wrong order?\n")); - } - m_root_p = boost::multiprecision::sqrt(modulus); - if (m_root_p*m_root_p != modulus) - { - m_root_p += 1; - } - - boost::optional x = mod_inverse(base, modulus); - if (!x) - { - Z d = boost::integer::gcd(base, modulus); - std::ostringstream oss; - oss << "The gcd of the base " << base << " and the modulus " << modulus << " is " << d - << ", which is not equal 1; hence the discrete log is not guaranteed to exist.\n" - << "This breaks the baby-step giant step algorithm.\n" - << "If you don't require existence for all inputs, use trial multiplication.\n"; - BOOST_THROW_EXCEPTION(std::logic_error(oss.str())); - } - m_inv_base_pow_m = boost::multiprecision::powm(x.value(), m_root_p, modulus); - - m_lookup_table.reserve(m_root_p); - // Now the expensive part: - Z k = 1; - for (Z j = 0; j < m_root_p; ++j) - { - m_lookup_table.emplace(k, j); - k = k*base % modulus; - } - - } - - boost::optional operator()(Z arg) const - { - Z ami = m_inv_base_pow_m; - Z k = arg % m_p; - if(k == 0) - { - return {}; - } - for (Z i = 0; i < m_lookup_table.size(); ++i) - { - auto it = m_lookup_table.find(k); - if (it != m_lookup_table.end()) - { - Z log_b_arg = (i*m_root_p + it->second) % m_p; - // This computation of the modular exponentiation is laughably quick relative to computing the discrete log. - // Why not put an assert here for our peace of mind? - BOOST_ASSERT(arg == boost::multiprecision::powm(m_base, log_b_arg, m_p)); - return log_b_arg; - } - ami = (ami*m_inv_base_pow_m) % m_p; - k = k * ami % m_p; - } - return {}; - } - -private: - Z m_p; - Z m_base; - Z m_root_p; - Z m_inv_base_pow_m; - std::unordered_map m_lookup_table; -}; - - -}} -#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c39b698..51d7092 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -16,7 +16,6 @@ test-suite integer [ run integer_mask_test.cpp ] [ run static_log2_test.cpp ] [ run static_min_max_test.cpp ] - [ run discrete_log_test.cpp ] [ run extended_euclidean_test.cpp ] [ run mod_inverse_test.cpp ] [ compile integer_traits_include_test.cpp ] diff --git a/test/discrete_log_test.cpp b/test/discrete_log_test.cpp deleted file mode 100644 index 7394419..0000000 --- a/test/discrete_log_test.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * (C) Copyright Nick Thompson 2018. - * Use, modification and distribution are subject to the - * Boost Software License, Version 1.0. (See accompanying file - * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ - -#define BOOST_TEST_MODULE discrete_log_test -#include -#include -#include - - -using boost::integer::trial_multiplication_discrete_log; -using boost::integer::bsgs_discrete_log; - -template -void test_trial_multiplication_discrete_log() -{ - std::cout << "Testing basic trial multiplication discrete logarithm on type " << boost::typeindex::type_id().pretty_name() << "\n"; - boost::optional x = trial_multiplication_discrete_log(2, 1, 3); - BOOST_CHECK_EQUAL(0, x.value()); - x = trial_multiplication_discrete_log(2, 2, 3); - BOOST_CHECK_EQUAL(1, x.value()); - - x = trial_multiplication_discrete_log(2, 1, 4); - BOOST_CHECK_EQUAL(0, x.value()); - - x = trial_multiplication_discrete_log(2, 2, 4); - BOOST_CHECK_EQUAL(1, x.value()); - - // No solution to 2^^x mod 4 = 3: - x = trial_multiplication_discrete_log(2, 3, 4); - BOOST_TEST(!x); - - x = trial_multiplication_discrete_log(7, 7, 41); - BOOST_CHECK_EQUAL(1, x.value()); - x = trial_multiplication_discrete_log(7, 8, 41); - BOOST_CHECK_EQUAL(2, x.value()); - x = trial_multiplication_discrete_log(7, 15, 41); - BOOST_CHECK_EQUAL(3, x.value()); - x = trial_multiplication_discrete_log(7, 23, 41); - BOOST_CHECK_EQUAL(4, x.value()); - x = trial_multiplication_discrete_log(7, 38, 41); - BOOST_CHECK_EQUAL(5, x.value()); - x = trial_multiplication_discrete_log(7, 20, 41); - BOOST_CHECK_EQUAL(6, x.value()); - - - Z k = 1; - for (Z i = 0; i < 20; ++i) - { - x = trial_multiplication_discrete_log(7, k, 41); - BOOST_CHECK_EQUAL(i, x.value()); - k = (7*k) % 41; - } -} - -template -void test_bsgs_discrete_log() -{ - std::cout << "Testing basic baby-step giant-step discrete logarithm on type " << boost::typeindex::type_id().pretty_name() << "\n"; - bsgs_discrete_log dl_7(7, 41); - BOOST_CHECK_EQUAL(dl_7(7).value(), 1); - BOOST_CHECK_EQUAL(dl_7(8).value(), 2); - BOOST_CHECK_EQUAL(dl_7(15).value(), 3); - BOOST_CHECK_EQUAL(dl_7(23).value(), 4); - BOOST_CHECK_EQUAL(dl_7(38).value(), 5); - BOOST_CHECK_EQUAL(dl_7(20).value(), 6); -} - -template -void test_trial_multiplication_with_prime_modulus() -{ - std::cout << "Testing trial multiplication with prime modulus on type " << boost::typeindex::type_id().pretty_name() << "\n"; - // There are roughly 10,000 primes in the boost.math prime table, - // so this test could run indefinitely. I leave the syntax to get the test to run - // for years as commented-out code, since ideally we would test every input. - for (Z i = 0; i < 10 /*boost::math::max_prime*/; ++i) - { - Z modulus = boost::math::prime(i); - for (Z base = 2; base < modulus; ++base) - { - for (Z arg = 1; arg < modulus; ++arg) - { - boost::optional dl = trial_multiplication_discrete_log(base, arg, modulus); - if (dl) - { - BOOST_CHECK_EQUAL(arg, boost::multiprecision::powm(base, dl.value(), modulus)); - } - } - } - } -} - - -template -void test_bsgs_with_prime_modulus() -{ - std::cout << "Testing baby-step, giant-step with prime modulus on type " << boost::typeindex::type_id().pretty_name() << "\n"; - for (Z i = 0; i < 10 /*boost::math::max_prime*/; ++i) - { - Z p = boost::math::prime(i); - for (Z j = 2; j < p; ++j) - { - bsgs_discrete_log dl_j(j, p); - for (Z k = 1; k < p; ++k) - { - boost::optional dl = dl_j(k); - if (dl) - { - BOOST_CHECK_EQUAL(k, boost::multiprecision::powm(j, dl.value(), p)); - } - } - } - } -} - -BOOST_AUTO_TEST_CASE(discrete_log_test) -{ - test_trial_multiplication_discrete_log(); - //test_bsgs_discrete_log(); - //test_trial_multiplication_with_prime_modulus(); - //test_bsgs_with_prime_modulus(); -} diff --git a/test/extended_euclidean_test.cpp b/test/extended_euclidean_test.cpp index 16fc0ff..a1509a7 100644 --- a/test/extended_euclidean_test.cpp +++ b/test/extended_euclidean_test.cpp @@ -26,7 +26,7 @@ void test_extended_euclidean() { for (Z n = m; n > 0; --n) { - boost::integer::euclidean_result_t u = extended_euclidean(m, n); + boost::integer::euclidean_result_t u = extended_euclidean(m, n); int256_t gcdmn = gcd(m, n); int256_t x = u.x; int256_t y = u.y;