функция pow и long int вызывают проблемы

Я пытаюсь внедрить схему шифрования RSA. Это выглядит примерно так:

encrypted data = ((message)^e) % n и decrypted data = ((encrypted data)^d) % n

Я попытался реализовать это в c. Вот код:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(){

    long int num = 3255859; 
    long int encrypt =(int)pow((double) num,3) % 33;
    printf("%ld\n",encrypt);

    return 0;

}

Я скомпилировал это, используя gcc -Werror -g -o encrypt encrypt.c -lm

Это результат, который я получаю = -2, что явно неверно. Когда я пробую этот код для меньших чисел, я получаю правильный результат. Например:

когда я устанавливаю num = 2, я получаю правильный результат: 8

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

Не могли бы вы указать, где я ошибаюсь.

Спасибо

РЕДАКТИРОВАТЬ:

Хорошо, согласно предложению @Micael Oliver, вот измененный код:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(){

    unsigned long long  num = 3255859; 

    long long encrypt =(long long)pow((double) num,3) % 33;

    printf("%llu\n",encrypt);

    long long decrypt =(long long)pow((double) encrypt,7) % 33;

    printf("%llu\n",decrypt);

    return 0;

}

вот вывод этого кода:

Notra:Desktop Sukhvir$ gcc -Werror -g -o encrypt encrypt.c -lm
Notra:Desktop Sukhvir$ ./encrypt
18446744073709551608
18446744073709551614

что явно неверно, так как 2-й выход должен был быть 3255859


person sukhvir    schedule 29.09.2013    source источник
comment
Попробуйте использовать long long, но только если вы ожидаете, что ваши числа останутся меньше 2^63, положительных или отрицательных.   -  person Michael Oliver    schedule 29.09.2013
comment
Я пробовал использовать long long int как для num, так и для encrypt... результат тот же = -2 :(   -  person sukhvir    schedule 29.09.2013
comment
Использование long long int несколько нестандартно. Обычно люди используют только long long. Кроме того, если вам нужны только положительные числа, используйте unsigned long long. Для long long можно использовать %lld, а для неподписанной версии использовать %llu.   -  person Michael Oliver    schedule 29.09.2013
comment
Вы должны использовать long long encrypt =(long long)pow((double) num,3) % 33;   -  person Michael Oliver    schedule 29.09.2013
comment
Однако с вашим решением все еще есть проблема. pow возвращает double, который обычно имеет только 15 цифр точности (надежно), даже если число может быть больше этого. Затем, когда вы конвертируете его в long long, вы не восстановите те цифры, которые вы потеряли, поэтому ваш мод может оказаться неправильным.   -  person Michael Oliver    schedule 29.09.2013
comment
это именно то, что происходит (см. мое редактирование исходного сообщения) ... как я могу исправить это. Возможно, есть другой способ возведения в степень в c ??   -  person sukhvir    schedule 29.09.2013
comment
Прежде всего, вы должны получить 34515102827311469 % 33 = 11. Я заметил, что у вас смешанные числа со знаком и без знака. %llu следует использовать только для беззнаковых. Что касается решения другой проблемы, то в C нет ничего встроенного. Вот статья в Википедии, в которой показан быстрый способ реализации pow: en.wikipedia.org/wiki/Exponentiation_by_squaring В противном случае вы можете изучить библиотеки, поддерживающие большие числа.   -  person Michael Oliver    schedule 29.09.2013


Ответы (3)


В вашем коде есть смесь беззнаковых и подписанных чисел - вы должны стараться избегать этого, когда это возможно. Также вы пытаетесь использовать %llu для подписанного длинного длинного - в этом случае вы должны использовать %lld.

Но здесь есть более тонкая проблема. В этой строке:

long long encrypt =(long long)pow((double) num,3) % 33;

pow возвращает double, что не гарантирует всей точности, которую вы ищете. В конечном итоге вы потеряете несколько цифр, когда будете приводить к long long. К сожалению, C не предоставляет хорошей альтернативы для вычисления экспонент, поэтому вам нужно будет что-то реализовать самостоятельно или использовать библиотеку (некоторые из других ответов предложили некоторые).

Если вы хотите реализовать его самостоятельно, в Википедии можно найти отличную статью о быстром возведении в степень с помощью возведения в квадрат: возведение в квадрат

Они предоставляют некоторый псевдокод, который должен быть очевиден для кодирования на C.

Но, наконец, в целом ваш код будет ограничен размером long long или любым другим типом, который вы выберете. В конечном счете, для больших чисел вы должны использовать другую библиотеку или найти лучший алгоритм. В этом случае вы вычисляете степень, а затем берете модуль — это именно то, что могут сделать алгоритмы модульного экспонирования без необходимости иметь дело с этими библиотеками. Вы можете найти статью в Википедии здесь: Modular Exponentiation

person Michael Oliver    schedule 29.09.2013

одно предложение состояло в том, чтобы использовать другой тип данных, например long long:

3255859^3 ==  34514116960466804779
ULLONG_MAX == 18446744073709551615  // this is the minimum guaranteed

Таким образом, unsigned long long может не работать. В целом изменение типов данных имеет ограничения. Другой более надежный подход, который вы можете рассмотреть, — это использование без GMP. руководство по gmp --

-- вы также можете скачать gmp на этом сайте.

фрагмент кода:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gmp.h>

int main()
{
    mpz_t rop, base, exp, mod;
    mpz_init2(rop,128); 
    mpz_init2(base,128); 
    mpz_init2(exp,128); 
    mpz_init2(mod,128);
    mpz_set_ui(base, 3255859);
    mpz_set_ui(exp, 3);
    mpz_set_ui(mod, 33);
    mpz_powm_sec (rop, base, exp, mod);
    gmp_printf ("result %Zd\n", rop);    
    return 0;
}
person jim mcnamara    schedule 29.09.2013
comment
Обратите внимание, что вам не нужно иметь возможность представлять num³, только num² в используемом типе. Это потому, что вы можете выполнять модульное сокращение после каждой операции (см. мой ответ). - person R.. GitHub STOP HELPING ICE; 29.09.2013

Пока ваши числа не превышают половины размера шрифта, с которым вы работаете, вы можете сделать что-то вроде этого:

(((num * num) % 33) * num) % 33

В общем, для чего-либо практичного в криптографических целях вам потребуются гораздо большие значения и вычислительная среда для работы с 1024+ битовыми числами. Для этого вы можете использовать существующий код (я бы рекомендовал libtommath из libtomcrypt, определенно не GMP) или написать свой собственный.

person R.. GitHub STOP HELPING ICE    schedule 29.09.2013
comment
Из любопытства, почему не GMP? - person Dmitri; 29.09.2013
comment
Потому что это небезопасно - это abort() ваша программа, если какая-либо попытка выделения не удалась. Это также неоправданно сложно и непереносимо. - person R.. GitHub STOP HELPING ICE; 29.09.2013
comment
LibTomMath не поддерживается - сайт github LibTom.org - person jim mcnamara; 29.09.2013
comment
Признаком хорошего программного обеспечения является то, что оно по-прежнему прекрасно работает даже после длительного отсутствия обновлений. :-) Серьезно, libtommath используется в реализации Dropbear SSH и различных других продуктах безопасности. Я не проводил детального аудита, но на основании того ограниченного чтения, которое я сделал, это высококачественный код, который позволяет читателю быстро понять, правильный ли он, а не запутывает все необоснованной сложностью. - person R.. GitHub STOP HELPING ICE; 29.09.2013