Упрощение модульного возведения в степень C++

Я пытаюсь написать функцию расшифровки для системы шифрования RSA, все работает нормально для очень небольших чисел, однако иногда вывод просто неверен (я думаю, что причиной может быть ошибка с плавающей запятой или какое-то переполнение стека).

Процесс, который вызывает у меня проблемы, можно упростить до (11^23) mod 187, но я включу полный код на случай, если кто-то захочет его увидеть. Я знаю, что ответ должен быть 88, так как это пример, использованный в Приложении J "Книги кодов" доктора Саймона Сингха (я также проверил с помощью Wolfram Alpha). Тем не менее, я получаю результат 149. Однако с меньшими числами это согласуется с Wolfram Alpha.

Я думаю, что мне нужно упростить модульное возведение в степень, используя знание того, что:

a^b = a^c * a^d [где c + d = b]

Тем не менее, я до сих пор не уверен на 100%, если это проблема, это мое первое переполнение стека? (Я все еще не уверен на 100%, что это значит). Прежде чем кто-нибудь попытается на меня ответить, нет, это не какое-то домашнее задание, и мне жаль, если этот вопрос кажется тривиальным. Я открыт для использования gmp.h, если все думают, что это будет слишком сложно сделать, но я бы предпочел не делать этого, если буду полностью честен. Мой код приведен ниже (первая половина предназначена для вычисления закрытого ключа, который, как я считаю, не имеет отношения к моей проблеме, но я включил его на случай, если я ошибаюсь), я действительно надеюсь, что вы, ребята, можете помочь, спасибо вы очень заранее.

#include <iostream>
#include <math.h>

using namespace std;

unsigned int modinv(unsigned int u, unsigned int v)
{
    unsigned int inv, u1, u3, v1, v3, t1, t3, q;
    int iter;

    u1 = 1;
    u3 = u;
    v1 = 0;
    v3 = v;

    iter = 1;

    while (v3 != 0)
    {

        q = u3 / v3;
        t3 = u3 % v3;
        t1 = u1 + q * v1;

        u1 = v1; v1 = t1; u3 = v3; v3 = t3;
        iter = -iter;
    }

    if (u3 != 1)
        return 0;
    if (iter < 0)
        inv = v - u1;
    else
        inv = u1;
    return inv;
}

int main()
{ long unsigned int p = 17;
long unsigned int q = 11;
long unsigned int phi = (p-1)*(q-1);
long unsigned int e = 7;
long unsigned int c = 11;
long unsigned int n = p*q;
long unsigned int d = modinv (e,phi);
    {
         cout << fmod (pow (c, d), n);
    }
    return 0;
}

person Michael    schedule 26.02.2014    source источник
comment
Может быть, 11^n mod 187 — плохой пример, поскольку 11 — один из простых множителей (187 = 11 x 17)? Таким образом, для 11^n mod 187 для n ›= 1 имеется только 16 значений в повторяющемся шаблоне: 11 121 22 55 44 110 88 33 176 66 165 132 143 77 99 154 || 11 121 ... . Итак, 11^n по модулю 187 = 11^(1+((n-1) по модулю 16)) по модулю 187.   -  person rcgldr    schedule 27.02.2014
comment
Я отсортировал его :) функция pow использует число с плавающей запятой, а не натуральное число, мне пришлось написать собственное модульное возведение в степень, которое сработало хорошо, все равно спасибо   -  person Michael    schedule 27.02.2014


Ответы (2)


11^23 примерно равно 2^80. Только целые числа до 2^53 могут быть представлены точно как двойные числа с плавающей запятой. Следовательно, fmod(pow(c, d), n)) возвращает приблизительное значение. Это не подходит для криптографии.

ДОБАВЛЕНО Вы можете выполнять модульное возведение в степень, используя повторное возведение в квадрат. Проверьте статью Википедии о «Возведении в степень путем возведения в квадрат».

person user515430    schedule 26.02.2014
comment
что было бы подходящей альтернативой? - person Michael; 27.02.2014
comment
Реализуйте свою собственную модульную функцию возведения в степень. Это довольно просто и требует меньше строк, чем ваш modinv. - person user515430; 27.02.2014
comment
так что нет существующего способа сделать это, мне придется написать свой собственный? Извините, если это покажется глупым вопросом. - person Michael; 27.02.2014
comment
Да, вам придется написать свой собственный. Это что-то вроде 14 строк. - person user515430; 27.02.2014
comment
int modExp(int b, int e, int m) { int remainder; int x = 1; while (e != 0) { remainder = e % 2; e= e/2; if (remainder == 1) x = (x * b) % m; b= (b * b) % m; // New base equal b^2 % m } это работает, спасибо :) - person Michael; 27.02.2014

Этот раздел вики-статьи о RSA должен помочь:

пример работы дешифрования RSA

Обратите внимание, что статья содержит ссылку на китайский алгоритм остатка, который содержит ссылку на алгоритм Евклида: по двум простым числам, p и q, найдите два целых числа a и b, так что a p + b q = 1, что также означает, что (a p) mod q == 1 и (b q) mod p == 1. Неясно, что либо a, либо b будут отрицательными, а отрицательное значение должно использоваться в первой части алгоритма остатка (в статье говорится использовать значения из алгоритма Евклида). Например, если a отрицательно, то (a p) mod q == 1, но ((a+q) p) mod q также == 1, так что и a, и a+q можно считать обратными p для математики по модулю q.

person rcgldr    schedule 27.02.2014