简体   繁体   English

计算 pow(a,b) mod n

[英]Calculating pow(a,b) mod n

I want to calculate a b mod n for use in RSA decryption.我想计算 a b mod n 以用于 RSA 解密。 My code (below) returns incorrect answers.我的代码(如下)返回不正确的答案。 What is wrong with it?它有什么问题?

unsigned long int decrypt2(int a,int b,int n)
{
    unsigned long int res = 1;

    for (int i = 0; i < (b / 2); i++)
    {
        res *= ((a * a) % n);
        res %= n;
    }

    if (b % n == 1)
        res *=a;

    res %=n;
    return res;
}

You can try this C++ code.你可以试试这个 C++ 代码。 I've used it with 32 and 64-bit integers.我已经将它用于 32 位和 64 位整数。 I'm sure I got this from SO.我确定我是从 SO 那里得到的。

template <typename T>
T modpow(T base, T exp, T modulus) {
  base %= modulus;
  T result = 1;
  while (exp > 0) {
    if (exp & 1) result = (result * base) % modulus;
    base = (base * base) % modulus;
    exp >>= 1;
  }
  return result;
}

You can find this algorithm and related discussion in the literature on p.您可以在 p.1 的文献中找到该算法和相关讨论。 244 of 244

Schneier, Bruce (1996).施奈尔,布鲁斯 (1996)。 Applied Cryptography: Protocols, Algorithms, and Source Code in C, Second Edition (2nd ed.).应用密码学:C 语言中的协议、算法和源代码,第二版(第 2 版)。 Wiley.威利。 ISBN 978-0-471-11709-4. ISBN 978-0-471-11709-4。


Note that the multiplications result * base and base * base are subject to overflow in this simplified version.请注意,在此简化版本中,乘法result * basebase * base可能会溢出。 If the modulus is more than half the width of T (ie more than the square root of the maximum T value), then one should use a suitable modular multiplication algorithm instead - see the answers to Ways to do modulo multiplication with primitive types .如果模数大于T宽度的一半(即大于最大T值的平方根),则应改用合适的模乘法算法 - 请参阅使用原始类型进行模乘法的方法的答案。

In order to calculate pow(a,b) % n to be used for RSA decryption, the best algorithm I came across is Primality Testing 1) which is as follows:为了计算用于 RSA 解密的pow(a,b) % n ,我遇到的最佳算法是Primality Testing 1) ,如下所示:

 int modulo(int a, int b, int n){
    long long x=1, y=a; 
    while (b > 0) {
        if (b%2 == 1) {
            x = (x*y) % n; // multiplying with base
        }
        y = (y*y) % n; // squaring the base
        b /= 2;
    }
    return x % n;
}

See below reference for more details.有关更多详细信息,请参阅以下参考资料。


1) Primality Testing : Non-deterministic Algorithms – topcoder 1) Primality Testing : Non-deterministic Algorithms – topcoder

Usually it's something like this:通常是这样的:

while (b)
{
    if (b % 2) { res = (res * a) % n; }

    a = (a * a) % n;
    b /= 2;
}

return res;

The only actual logic error that I see is this line:我看到的唯一实际逻辑错误是这一行:

if (b % n == 1)

which should be this:应该是这样的:

if (b % 2 == 1)

But your overall design is problematic: your function performs O(b) multiplications and modulus operations, but your use of b / 2 and a * a implies that you were aiming to perform O(log b) operations (which is usually how modular exponentiation is done).但是您的整体设计是有问题的:您的函数执行 O(b) 乘法和模运算,但是您使用b / 2a * a意味着您的目标是执行 O(log b) 运算(这通常是模幂运算的方式)已经完成了)。

Doing the raw power operation is very costly, hence you can apply the following logic to simplify the decryption.执行原始功率操作的成本非常高,因此您可以应用以下逻辑来简化解密。

From here ,这里

Now say we want to encrypt the message m = 7,现在假设我们要加密消息 m = 7,
c = m^e mod n = 7^3 mod 33 = 343 mod 33 = 13. c = m^e mod n = 7^3 mod 33 = 343 mod 33 = 13。
Hence the ciphertext c = 13.因此密文 c = 13。

To check decryption we compute为了检查解密,我们计算
m' = c^d mod n = 13^7 mod 33 = 7. m' = c^d mod n = 13^7 mod 33 = 7。
Note that we don't have to calculate the full value of 13 to the power 7 here.请注意,我们不必在这里计算 13 的 7 次方的完整值。 We can make use of the fact that我们可以利用以下事实
a = bc mod n = (b mod n).(c mod n) mod n a = bc mod n = (b mod n).(c mod n) mod n
so we can break down a potentially large number into its components and combine the results of easier, smaller calculations to calculate the final value.所以我们可以将一个潜在的大数分解成它的组成部分,并结合更简单、更小计算的结果来计算最终值。

One way of calculating m' is as follows:-一种计算 m' 的方法如下:-
Note that any number can be expressed as a sum of powers of 2. So first compute values of请注意,任何数字都可以表示为 2 的幂之和。 所以首先计算
13^2, 13^4, 13^8, ... by repeatedly squaring successive values modulo 33. 13^2 = 169 ≡ 4, 13^4 = 4.4 = 16, 13^8 = 16.16 = 256 ≡ 25. 13^2, 13^4, 13^8, ... 通过反复对连续值取模 33 进行平方。13^2 = 169 ≡ 4, 13^4 = 4.4 = 16, 13^8 = 16.16 = 256 ≡ 25。
Then, since 7 = 4 + 2 + 1, we have m' = 13^7 = 13^(4+2+1) = 13^4.13^2.13^1然后,由于 7 = 4 + 2 + 1,我们有 m' = 13^7 = 13^(4+2+1) = 13^4.13^2.13^1
≡ 16 x 4 x 13 = 832 ≡ 7 mod 33 ≡ 16 x 4 x 13 = 832 ≡ 7 mod 33

Calculating pow(a,b) mod n计算 pow(a,b) mod n

  1. A key problem with OP's code is a * a . OP 代码的一个关键问题是a * a This is int overflow (undefined behavior) when a is large enough.a足够大时,这是int溢出(未定义行为)。 The type of res is irrelevant in the multiplication of a * a . res的类型与a * a的乘法无关。

    The solution is to ensure either:解决方案是确保:

    • the multiplication is done with 2x wide math or乘法是用 2 倍宽的数学运算完成的,或者
    • with modulus n , n*n <= type_MAX + 1模数n , n*n <= type_MAX + 1
  2. There is no reason to return a wider type than the type of the modulus as the result is always represent by that type.没有理由返回比模数类型更宽的类型,因为结果总是由该类型表示。

     // unsigned long int decrypt2(int a,int b,int n) int decrypt2(int a,int b,int n)
  3. Using unsigned math is certainly more suitable for OP's RSA goals.使用无符号数学当然更适合 OP 的 RSA 目标。


Also see Modular exponentiation without range restriction另请参阅无范围限制的模幂运算

// (a^b)%n
// n != 0

// Test if unsigned long long at least 2x values bits as unsigned
#if ULLONG_MAX/UINT_MAX  - 1 > UINT_MAX
unsigned decrypt2(unsigned a, unsigned b, unsigned n) {
  unsigned long long result = 1u % n;  // Insure result < n, even when n==1
  while (b > 0) {
    if (b & 1) result = (result * a) % n;
    a = (1ULL * a * a) %n;
    b >>= 1;
  }
  return (unsigned) result;
}

#else
unsigned decrypt2(unsigned a, unsigned b, unsigned n) {
  // Detect if  UINT_MAX + 1 < n*n
  if (UINT_MAX/n < n-1) {
    return TBD_code_with_wider_math(a,b,n);
  }
  a %= n;
  unsigned result = 1u % n;
  while (b > 0) {
    if (b & 1) result = (result * a) % n;
    a = (a * a) % n;
    b >>= 1;
  }
  return result;
}

#endif

Are you trying to calculate (a^b)%n , or a^(b%n) ?您是要计算(a^b)%n还是a^(b%n)

If you want the first one, then your code only works when b is an even number, because of that b/2 .如果您想要第一个,那么您的代码仅在b是偶数时才有效,因为b/2 The " if b%n==1 " is incorrect because you don't care about b%n here, but rather about b%2 . if b%n==1 ”是不正确的,因为您在这里不关心b%n ,而是关心b%2

If you want the second one, then the loop is wrong because you're looping b/2 times instead of (b%n)/2 times.如果你想要第二个,那么循环是错误的,因为你循环了b/2次而不是(b%n)/2次。

Either way, your function is unnecessarily complex.无论哪种方式,您的功能都不必要地复杂。 Why do you loop until b/2 and try to multiply in 2 a's each time?为什么要循环直到b/2并尝试每次乘以 2 a? Why not just loop until b and mulitply in one a each time.为什么不循环直到b并且每次都乘以一个 a。 That would eliminate a lot of unnecessary complexity and thus eliminate potential errors.这将消除许多不必要的复杂性,从而消除潜在的错误。 Are you thinking that you'll make the program faster by cutting the number of times through the loop in half?您是否认为通过将循环次数减半可以使程序更快? Frankly, that's a bad programming practice: micro-optimization.坦率地说,这是一个糟糕的编程实践:微优化。 It doesn't really help much: You still multiply by a the same number of times, all you do is cut down on the number of times testing the loop.它并没有多大帮助:您仍然乘以相同的次数,您所做的只是减少测试循环的次数。 If b is typically small (like one or two digits), it's not worth the trouble.如果 b 通常很小(比如一位或两位数字),那就不值得麻烦了。 If b is large -- if it can be in the millions -- then this is insufficient, you need a much more radical optimization.如果 b 很大——如果它可以达到数百万——那么这还不够,你需要更彻底的优化。

Also, why do the %n each time through the loop?另外,为什么每次都执行%n循环? Why not just do it once at the end?为什么不只在最后做一次?

Here is another way.这是另一种方式。 Remember that when we find modulo multiplicative inverse of a under mod m .请记住,当我们发现modulo multiplicative inversea下模m Then然后

a and m must be coprime with each other. am必须coprime

We can use gcd extended for calculating modulo multiplicative inverse .我们可以使用gcd extended来计算modulo multiplicative inverse

For computing a b mod m when a and b can have more than 10 5 digits then its tricky to compute the result.ab可以有超过 10 个5位时计算a b mod m则计算结果很棘手。

Below code will do the computing part :下面的代码将完成计算部分:

#include <iostream>
#include <string>
using namespace std;
/*
*   May this code live long.
*/
long pow(string,string,long long);
long pow(long long ,long long ,long long);
int main() {
    string _num,_pow;
    long long _mod;
    cin>>_num>>_pow>>_mod;
    //cout<<_num<<" "<<_pow<<" "<<_mod<<endl;
    cout<<pow(_num,_pow,_mod)<<endl;
   return 0;
}
long pow(string n,string p,long long mod){
    long long num=0,_pow=0;
    for(char c: n){
        num=(num*10+c-48)%mod;
    }
    for(char c: p){
        _pow=(_pow*10+c-48)%(mod-1);
    }
    return pow(num,_pow,mod);
}
long pow(long long a,long long p,long long mod){
    long res=1;
    if(a==0)return 0;
    while(p>0){
        if((p&1)==0){
            p/=2;
            a=(a*a)%mod;
        }
        else{
            p--;
            res=(res*a)%mod;
        }
    }
    return res;
}
 

This code works because a b mod m can be written as (a mod m) b mod m-1 mod m .此代码有效,因为a b mod m可以写为(a mod m) b mod m-1 mod m

Hope it helped { :)希望它有帮助{ :)

int 's are generally not enough for RSA (unless you are dealing with small simplified examples) int对 RSA 来说通常是不够的(除非您正在处理小的简化示例)

you need a data type that can store integers up to 2 256 (for 256-bit RSA keys) or 2 512 for 512-bit keys, etc您需要一种可以存储高达 2 256 (对于 256 位 RSA 密钥)或 2 512 的整数(用于 512 位密钥等)的数据类型

use fast exponentiation maybe..... gives same o(log n) as that template above使用快速求幂可能..... 给出与上面那个模板相同的 o(log n)

    int power(int base, int exp,int mod)
{
    if(exp == 0)
     return 1;

    int p=power(base, exp/2,mod);
    p=(p*p)% mod;
    return (exp%2 == 0)?p:(base * p)%mod;
}

This(encryption) is more of an algorithm design problem than a programming one.这(加密)更像是一个算法设计问题,而不是一个编程问题。 The important missing part is familiarity with modern algebra.重要的缺失部分是对现代代数的熟悉。 I suggest that you look for a huge optimizatin in group theory and number theory.我建议你在群论和数论中寻找一个巨大的优化。 If n is a prime number, pow(a,n-1)%n==1 (assuming infinite digit integers).So, basically you need to calculate pow(a,b%(n-1))%n ;如果n是素数,则pow(a,n-1)%n==1 (假设为无穷位整数)。因此,基本上您需要计算pow(a,b%(n-1))%n ; According to group theory, you can find e such that every other number is equivalent to a power of e modulo n .根据群论,您可以找到e使得每隔一个数字都等价于en的幂。 Therefore the range [1..n-1] can be represented as a permutation on powers of e .因此,范围[1..n-1]可以表示为e幂的排列。 Given the algorithm to find e for n and logarithm of a base e , calculations can be significantly simplified.由于算法来寻找en和对数a基地e ,计算可以显著简化。 Cryptography needs a tone of math background;密码学需要数学背景; I'd rather be off that ground without enough background.我宁愿在没有足够背景的情况下离开那个地方。

For my code a^k mod n in php:对于我的代码a^k mod n在 php 中:

function pmod(a, k, n)
{
    if (n==1) return 0;
    power = 1;
    for(i=1; i<=k; $i++)
    {
        power = (power*a) % n;
    }
    return power;
}

I'm using this function:我正在使用这个功能:

int CalculateMod(int base, int exp ,int mod){
    int result;
    result = (int) pow(base,exp);
    result = result % mod;
    return result;
}

I parse the variable result because pow give you back a double, and for using mod you need two variables of type int, anyway, in a RSA decryption, you should just use integer numbers.我解析变量结果是因为 pow 给你一个双精度数,并且为了使用 mod 你需要两个 int 类型的变量,无论如何,在 RSA 解密中,你应该只使用整数。

#include <cmath>
...
static_cast<int>(std::pow(a,b))%n

but my best bet is you are overflowing int (IE: the number is two large for the int) on the power I had the same problem creating the exact same function.但我最好的赌注是你在电源上溢出了 int(即:int 的数字是两个大的)我在创建完全相同的函数时遇到了同样的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM