【问题标题】:pow function and long int causing problemspow 函数和 long int 导致问题
【发布时间】:2013-10-05 02:10:03
【问题描述】:

我正在尝试实施 RSA 加密方案。它是这样的:

encrypted data = ((message)^e) % ndecrypted 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

这显然是错误的,因为第二个输出应该是 3255859

【问题讨论】:

  • 尝试使用long long,但前提是您希望您的数字保持在 2^63 以下,无论是正数还是负数。
  • 我在 num 和 encrypt 上都尝试了 long long int ... 结果仍然相同 = -2 :(
  • 使用long long int 有点不合标准。通常人们只使用long long。此外,如果您只想要正数,请使用unsigned long long。对于long long,您可以使用%lld,对于未签名版本使用%llu
  • 你应该使用long long encrypt =(long long)pow((double) num,3) % 33;
  • 您的解决方案仍然存在问题。 pow 返回一个 double,它通常只有 15 位精度(可靠),即使数字可能比这更大。然后,当您将其转换为 long long 时,您将无法重新获得丢失的数字,因此您的 mod 最终可能会出错。

标签: c gcc encryption int pow


【解决方案1】:

一个建议是使用另一种数据类型,如 long long:

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

因此,unsigned long long 可能不起作用。一般来说,改变数据类型是有限制的。 您可以考虑的另一种更强大的方法是使用 GMP - free。 gmp manual--

--你也可以在这个站点下载gmp。

代码sn-p:

#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;
}

【讨论】:

  • 请注意,您不需要能够在使用的类型中表示 num³,只需 num²。这是因为您可以在每次操作后执行模块化归约(请参阅我的回答)。
【解决方案2】:

只要你的数字最多是你正在使用的类型大小的一半,你就可以这样做:

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

一般来说,对于任何实用的加密目的,您都需要更大的值和一个计算框架来处理 1024 位以上的数字。为此,您可以使用现有代码(我会推荐来自libtomcryptlibtommath,绝对不是 GMP)或编写自己的代码。

【讨论】:

  • 出于好奇,为什么不是 GMP?
  • 因为它不安全 - 如果任何分配尝试失败,它abort()s 你的程序。它也非常复杂且不可移植。
  • LibTomMath 似乎不受支持 - github site LibTom.org
  • 好软件的一个标志是它在很长一段时间没有更新后仍然可以完美使用。 :-) 说真的,libtommath 用于 Dropbear SSH 实现和各种其他安全产品。我没有对它进行详细的审核,但基于我所做的有限阅读,它是高质量的代码,可以让读者快速清楚它是否正确,而不是用无端的复杂性来混淆所有内容。
【解决方案3】:

您的代码中混合了一些无符号和有符号数字 - 您应该尽可能避免这种情况。此外,您还尝试在有符号的 long long 上使用 %llu - 在这种情况下,您应该使用 %lld

但这里还有一个更微妙的问题。在这一行:

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

pow 返回一个double,它不能保证您正在寻找的所有精度。当您投射到long long 时,您最终会丢失几位数字。不幸的是,C 并没有为计算指数提供一个很好的替代方案,所以你需要自己实现一些东西或使用一个库(其他一些答案已经提出了一些建议)。

如果您想自己实现一个,可以在 Wikipedia 上找到一篇关于平方快速取幂的精彩文章:Exponentiation by squaring

它们提供了一些在 C 语言中应该很明显的伪代码。

但最后,一般而言,您的代码将受到long long 大小或您选择的任何类型的限制。最终,对于大量数据,您应该使用其他库,或者找到更好的算法。在这种情况下,您要计算幂,然后取模——这正是模指数算法无需处理这些库即可完成的工作。你可以在这里找到一篇维基百科文章:Modular Exponentiation

【讨论】:

    猜你喜欢
    • 2012-01-27
    • 2021-05-06
    • 2018-10-20
    • 1970-01-01
    • 2015-09-27
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多