Криптография и генерация больших однозначно простых чисел — критерий Поклингтона

Введение

В этой статье мы рассмотрим итеративный алгоритм по генерации больших однозначно простых чисел больше заданного порядка, который использует критерий Поклингтона.
Алгоритм использует простое число меньшего порядка как минимум удваивая количество цифр для следующего шага.

О применение простых чисел в криптографии и не только можно прочитать здесь, а в данной статье сконцентрируемся на самом алгоритме.

Подходы к генерации простых чисел

И так пусть у нас есть заданное число Nпорядка больше 10^{35}, и мы хотим получить простое число p:p>=N» src=«https://habrastorage.org/getpro/habr/upload_files/2c2/a7f/b9e/2c2a7fb9ecfa1240c44429a04a9fc567.svg» />.</p>

<ol><li><p>Для начала мы можем воспользоваться одним из решето-алгоритмов для получения всех простых чисел до заданного <img alt=— Эратосфена, Аткина, Сундарама;

  • Второй подход это генерация рандомных нечётных чисел больше N и проверка каждого из них на простоту через перебор делителей, вероятностные и истинные тесты простоты — подходы можно глянуть здесь;

  • Наконец третий подход это комбинирование элементов из подходов выше для создания более комплексных алгоритмов;

  • Описание алгоритма

    Рассмотрим алгоритм из 3 подхода, который комбинирует решето Эратосфена для получения первичных простых чисел и критерий Поклингтона, который в свою очередь использует малую теорему Ферма, для получения однозначно простого числа. Как было сказано выше, алгоритм итеративный, то есть мы получаем большее простое nиз текущего s.

    1. Строим решето Эратосфена до k, где k это некая константа — например 10^3. Выбираем стартовое простое число s— либо принимаем аргументом, либо из построенного решета. Переходим к шагу 2.

    2. Имеем простое число s— если s>N» src=«https://habrastorage.org/getpro/habr/upload_files/a5d/07f/fe6/a5d07ffe62c8f0dcd030aefa95f0373b.svg» />, то результат алгоритма это <img alt=, иначе мы хотим найти простое число n:n>s» src=«https://habrastorage.org/getpro/habr/upload_files/937/f83/ec4/937f83ec402fe0a28178fa392a116c80.svg» /> — переходим к шагу 3.</p></li><li><p>Выбираем рандомно чётное число<img alt=и запишем кандидат на простоту n=s*r+1. Переходим к шагу 4.

    3. Проверяем n на делимость с простыми числами низкого порядка, полученными на шаге 1 — если число делится на одно из них, то оно составное и возвращаемся к выбору нового кандидата n, то есть шагу 2. Иначе число может быть простым, поэтому переходим к шагу 5.

    4. Выбираем рандомно число a:1<a<n и проверяем для нашего кандидата на простоту nисполнимость следующих двух условий a^{n-1}  \equiv 1\:(mod\:n)и gcd(a^r-1,n)==1.

      Если оба исполняются, то по критерию Поклингтона число n простое — заменяем s:=n и переходим к следующей итерации по поиску большего числа, то есть шагу 2.

      Иначе если не исполняется первое условие — a^{n-1}\equiv1\:(mod\:n), то по малой теореме Ферма число n не является простым, поэтому переходим к выбору нового кандидата, то есть шагу 3.

      Иначе если не исполняется вторая часть, то анализ немного сложнее — мы нашли делитель d:1<d<=nдля кандидата n, посколькуgcd(a^r-1,n)==d. Предположим, что d ≠n, что подразумевает не примитивный делитель, а значит nне простое — переходим к повторному выбору, то есть шагу 3.

      Остаётся случай, когда d==n, что означает a^r\equiv1\:(mod\:n), а решений этой конгруэнции существует не больше r. Одно из решений это x=1, поэтому на интервале 1<a<nсуществует не болееr-1решений, следовательно при действительно простом n мы найдём (можно оценить вероятность от s) такое a, которое бы удовлетворяло критерию Поклингтона, поэтому переходим к шагу 5 для повтора выбора a.

    Корректность алгоритма

    Можно заметить, что полученное на каждой итерации простое число будет не меньше квадрата предыдущего, то есть иметь в два раза больше цифр — выполняя шаги описанные выше мы дойдём к простому числу больше N. Из этого так же следует, что количество цифр в результирующем простом числеp:p>N» src=«https://habrastorage.org/getpro/habr/upload_files/3ee/757/42d/3ee75742dee6ce3dc1facd1060adafe2.svg» />зависит от выбора начального простого числа для алгоритма, поэтому если мы хотим сделать <img alt=порядка N, то нужно это учесть.

    Исследование корректности и эффективности данного алгоритма выходит за пределы этой статьи, однако алгоритм имеет место быть из-за исследований Дирихле о простых числах в арифметических прогрессиях, исследований Люстерника о первом простом числе в такой прогрессии и гипотезы Крамера о расстояниях между двумя последовательными простыми числами.

    Код алгоритма на Python

    def generatePrime(n : int, primes = None, s = None):
        """Generates prime number with at least n digits:
    
        : param n: number of 10-based digits in the generate prime is at least n;
        : param primes: an iterable object of numbers that are used as small factors
        for pre-prime verification. If None, is computed using getSieve(1000);
        : param s: initial prime number - if None, last from primes is used;
        """
    
        # Any prime number higher than the up_limit suffices the result.
        up_limit = 10**n
    
        # Get the list of small primes which are used as small divisors
        # to reject the n candidate before the Pocklington test.
        if not primes: primes = getSieve(1000)
    
        if not s: s = primes[-1] # initial prime
        while s < up_limit:
            lo, hi = (s + 1) >> 1, (s << 1) + 1
    
            # Proceed with finding new prime n
            while True:
                r = random.randint(lo, hi) << 1 # get random even r from [s, 4*s + 2]
                n = s*r + 1 # n is prime candidate, s^2 <= n <= (4*s+2)*s + 1
    
                # reject n if n divides some number in primes list
                if not is_probably_prime(n, primes): continue
    
                # Here n is probably prime - apply Pocklington criterion to verify it
                while True:
                    a = random.randint(2, n-1)
    
                    # Fermat’s little theorem isn't satisfied - choose another n
                    if pow(a, n-1, n) != 1: break
    
                    d = math.gcd((pow(a, r, n) - 1) % n, n)
                    if d != n:
                        if d == 1: s = n # n is prime, replace s with n
                        else: pass # n isn't prime, choose another n
                        break
                    else: pass # a^r mod n == 1, choose another a
                if s == n: break
        return s

    После нескольких запусков с разным аргументом n получаем 5 простых чисел:

    1)	P1 = 222479360228659844149346639882089160021, D1 = 39
    2)	P2 = 327235960958148645696052834806967763219, D2 = 39
    3)	P3 = 1703805325300022851813841485118972214405495022945891, D3 = 52
    4)	P4 = 848995467487101811203366361379372085728608261197707959, D4 = 54
    5)	P5 = 1041875824682281112078115198781702612619843793759431, D5 = 52

    В конце убеждаемся в простоте через sympy.ntheory.primetest.isprime.

    Заключение

    Целью данной статьи было показать читателю эффективный итеративный алгоритм по получению больших однозначно простых чисел, который использует другие алгоритмы на числах — решето Эратосфена, вознесение в степень по модулю, малая теорема Ферма, критерий Поклингтона.

    Названия этого алгоритма я не нашёл, поэтому укажите в комментариях, если кто-то знает. Весь код можно глянуть в репозитории.

    © Habrahabr.ru