Path: blob/master/attacks/lcg/truncated_parameter_recovery.py
2589 views
import logging1import os2import sys3from itertools import combinations4from math import ceil5from math import gcd6from math import sqrt78from sage.all import ZZ9from sage.all import Zmod10from sage.all import factor11from sage.all import matrix1213path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))14if sys.path[1] != path:15sys.path.insert(1, path)1617from attacks.hnp import lattice_attack18from shared.lattice import shortest_vectors19from shared.polynomial import polynomial_gcd_crt202122# Section 2.1 in "On Stern's Attack Against Secret Truncated Linear Congruential Generators".23def _generate_polynomials(y, n, t):24B = matrix(ZZ, n, n + t)25for i in range(n):26for j in range(t):27B[i, j] = y[i + j + 1] - y[i + j]2829B[i, t + i] = 13031x = ZZ["x"].gen()32for v in shortest_vectors(B):33P = 034for i, l in enumerate(v[t:]):35P += l * x ** i36yield P373839# Section 4 in "On Stern's Attack Against Secret Truncated Linear Congruential Generators".40def _recover_modulus_and_multiplier(polynomials, m=None, a=None, check_modulus=None):41for comb in combinations(polynomials, 3):42P0 = comb[0]43P1 = comb[1]44P2 = comb[2]45m_ = gcd(P0.resultant(P1), P1.resultant(P2), P0.resultant(P2))46if (m is None and check_modulus(m_)) or m_ == m:47if a is None:48factors = factor(m_)49g = polynomial_gcd_crt(P0, polynomial_gcd_crt(P1, P2, factors), factors)50for a_ in g.change_ring(Zmod(m_)).roots(multiplicities=False):51yield int(m_), int(a_)52else:53yield int(m_), a545556# Generates possible values for the modulus, multiplier, increment, and seed.57# This is similar to the Hidden Number Problem, but with two 'global' unknowns.58def _recover_increment_and_seed(y, k, s, m, a):59a_ = []60b_ = []61X = 2 ** (k - s)62mult1 = a63mult2 = 164for i in range(len(y)):65a_.append([mult1, mult2])66b_.append(-X * y[i])67mult1 = (a * mult1) % m68mult2 = (a * mult2 + 1) % m6970for _, (x0_, c_) in lattice_attack.attack(a_, b_, m, X):71yield m, a, c_, x0_727374def attack(y, k, s, m=None, a=None, check_modulus=None):75"""76Recovers possible parameters and states from a truncated linear congruential generator.77More information: Contini S., Shparlinski I. E., "On Stern's Attack Against Secret Truncated Linear Congruential Generators"78If no modulus is provided, attempts to recover a modulus from the outputs.79If no multiplier is provided, attempts to recover a multiplier from the outputs.80Also recovers an increment from the outputs.81The resulting parameters may not match the original parameters, but the generated sequence should be the same up to some small error.82:param y: the sequential output values obtained from the truncated LCG (the states truncated to s most significant bits)83:param k: the bit length of the states84:param s: the bit length of the outputs85:param m: the modulus of the LCG (can be None)86:param a: the multiplier of the LCG (can be None)87:param check_modulus: a function which checks if a possible value can be the modulus (default: compare the bit length with k)88:return: a generator generating possible parameters (tuples of modulus, multiplier, increment, and seed) of the truncated LCG89"""90if m is None or a is None:91alpha = s / k92t = int(1 / alpha)93n = ceil(sqrt(2 * alpha * t * k))9495# We start at the minimum useful chunk size.96chunk_size = n + t97while chunk_size <= len(y):98logging.info(f"Trying chunk size {chunk_size}...")99polynomials = []100for i in range(len(y) // chunk_size):101logging.info(f"Generating polynomials for {n = }, {t = }...")102for P in _generate_polynomials(y[chunk_size * i:chunk_size * (i + 1)], n, t):103polynomials.append(P)104105logging.info("Recovering modulus and multiplier...")106for m_, a_ in _recover_modulus_and_multiplier(polynomials, m, a, check_modulus or (lambda m_: m_.bit_length() == k)):107logging.info("Recovering increment and seed...")108yield from _recover_increment_and_seed(y, k, s, m_, a_)109110t += 1111n = ceil(sqrt(2 * alpha * t * k))112chunk_size = n + t113else:114logging.info("Recovering increment and seed...")115yield from _recover_increment_and_seed(y, k, s, m, a)116117118