Baby-step giant-step
In group theory, a branch of mathematics, the baby-step giant-step is a meet-in-the-middle algorithm for computing the discrete logarithm. The discrete log problem is of fundamental importance to the area of public key cryptography. Many of the most commonly used cryptography systems are based on the assumption that the discrete log is extremely difficult to compute; the more difficult it is, the more security it provides a data transfer. One way to increase the difficulty of the discrete log problem is to base the cryptosystem on a larger group.
Contents
1 Theory
2 The algorithm
2.1 C++ algorithm
3 In practice
4 Notes
5 References
Theory
The algorithm is based on a space–time tradeoff. It is a fairly simple modification of trial multiplication, the naive method of finding discrete logarithms.
Given a cyclic group G{displaystyle G} of order n{displaystyle n}, a generator α{displaystyle alpha } of the group and a group element β{displaystyle beta }, the problem is to find an integer x{displaystyle x} such that
- αx=β.{displaystyle alpha ^{x}=beta ,.}
The baby-step giant-step algorithm is based on rewriting x{displaystyle x}:
- x=im+j{displaystyle x=im+j}
- m=⌈n⌉{displaystyle m=leftlceil {sqrt {n}}rightrceil }
- 0≤i<m{displaystyle 0leq i<m}
- 0≤j<m{displaystyle 0leq j<m}
Therefore, we have:
- αx=β{displaystyle alpha ^{x}=beta ,}
- αim+j=β{displaystyle alpha ^{im+j}=beta ,}
- αj=β(α−m)i{displaystyle alpha ^{j}=beta left(alpha ^{-m}right)^{i},}
The algorithm precomputes αj{displaystyle alpha ^{j}} for several values of j{displaystyle j}. Then it fixes an m{displaystyle m} and tries values of i{displaystyle i} in the right-hand side of the congruence above, in the manner of trial multiplication. It tests to see if the congruence is satisfied for any value of j{displaystyle j}, using the precomputed values of αj{displaystyle alpha ^{j}}.
The algorithm
Input: A cyclic group G of order n, having a generator α and an element β.
Output: A value x satisfying αx=β{displaystyle alpha ^{x}=beta }.
m ← Ceiling(√n)- For all j where 0 ≤ j < m:
- Compute αj and store the pair (j, αj) in a table. (See section "In practice")
- Compute α−m.
- γ ← β. (set γ = β)
- For all i where 0 ≤ i < m:
- Check to see if γ is the second component (αj) of any pair in the table.
- If so, return im + j.
- If not, γ ← γ • α−m.
C++ algorithm
#include <cmath>
#include <cstdint>
#include <unordered_map>
std::uint32_t pow_m(std::uint32_t base, std::uint32_t exp, std::uint32_t mod) {
// modular exponentiation using the square-multiply-algorithm
}
/// Computes x such that g^x % mod == h
std::optional<std::uint32_t> babystep_giantstep(std::uint32_t g, std::uint32_t h, std::uint32_t mod) {
const auto m = static_cast<std::uint32_t>(std::ceil(std::sqrt(mod)));
auto table = std::unordered_map<std::uint32_t, std::uint32_t>{};
auto e = std::uint64_t{1}; // temporary values may be bigger than 32 bit
for (auto i = std::uint32_t{0}; i < m; ++i) {
table[static_cast<std::uint32_t>(e)] = i;
e = (e * g) % mod;
}
const auto factor = pow_m(g, mod-m-1, mod);
e = h;
for (auto i = std::uint32_t{}; i < m; ++i) {
if (auto it = table.find(static_cast<std::uint32_t>(e)); it != table.end()) {
return {i*m + it->second};
}
e = (e * factor) % mod;
}
return std::nullopt;
}
In practice
The best way to speed up the baby-step giant-step algorithm is to use an efficient table lookup scheme. The best in this case is a hash table. The hashing is done on the second component, and to perform the check in step 1 of the main loop, γ is hashed and the resulting memory address checked. Since hash tables can retrieve and add elements in O(1){displaystyle O(1)} time (constant time), this does not slow down the overall baby-step giant-step algorithm.
The running time of the algorithm and the space complexity is O(n){displaystyle O({sqrt {n}})}, much better than the O(n){displaystyle O(n)} running time of the naive brute force calculation.
The Baby-step giant-step algorithm is often used to solve for the shared key in the Diffie Hellman key exchange, when the modulus is a prime number. If the modulus is not prime, the Pohlig–Hellman algorithm has a smaller algorithmic complexity, and solves the same problem.
Notes
- The baby-step giant-step algorithm is a generic algorithm. It works for every finite cyclic group.
- It is not necessary to know the order of the group G in advance. The algorithm still works if n is merely an upper bound on the group order.
- Usually the baby-step giant-step algorithm is used for groups whose order is prime. If the order of the group is composite then the Pohlig–Hellman algorithm is more efficient.
- The algorithm requires O(m) memory. It is possible to use less memory by choosing a smaller m in the first step of the algorithm. Doing so increases the running time, which then is O(n/m). Alternatively one can use Pollard's rho algorithm for logarithms, which has about the same running time as the baby-step giant-step algorithm, but only a small memory requirement.
- The algorithm is usually credited to Daniel Shanks, but a 1994 paper by Nechaev[1] states it was known to Gel'fond in 1962.
References
^ V. I. Nechaev, Complexity of a determinate algorithm for the discrete logarithm, Mathematical Notes, vol. 55, no. 2 1994 (165-172)
- H. Cohen, A course in computational algebraic number theory, Springer, 1996.
- D. Shanks. Class number, a theory of factorization and genera. In Proc. Symp. Pure Math. 20, pages 415—440. AMS, Providence, R.I., 1971.
- A. Stein and E. Teske, Optimized baby step-giant step methods, Journal of the Ramanujan Mathematical Society 20 (2005), no. 1, 1–32.
- A. V. Sutherland, Order computations in generic groups, PhD thesis, M.I.T., 2007.
- D. C. Terr, A modification of Shanks’ baby-step giant-step algorithm, Mathematics of Computation 69 (2000), 767–773. doi:10.1090/S0025-5718-99-01141-2