1
#include <iostream>
#include <bits/stdc++.h>
#include <numeric>

using namespace std;

int
gcd (int a, int b)
{
  if (a == 0)
    return b;
  return gcd (b % a, a);
}

int
phi (unsigned int n)
{
  unsigned int result = 1;
  for (int i = 2; i < n; i++)
    if (gcd (i, n) == 1)
      result++;
  return result;
}

int
gen_priv_n (int p1, int p2)
{
  int n = phi (p1) * phi (p2);
  return n;
}

int
gen_pub_n (int p1, int p2)
{
  int n = (p1 * p2);
  return n;
}

int
gen_priv_key (int n, int e)
{
  int x = (phi (e) * n + 1) / e;
  return x;
}

int
encrypt (int n, int e, int data)
{
  int crypt_data = int (pow (data, e)) % n;
  return crypt_data;
}

int
decrypt (int c, int d, int n)
{
  int data = int (pow (c, d)) % n;
  return data;
}

int
main ()
{
  int ph1 = 53;
  int ph2 = 59;
  int e = 3;
  int message = 89;
  
  int pub_n = gen_pub_n (ph1, ph2);
  cout << "PUBN " << pub_n << "\n";
  int priv_n = gen_priv_n (ph1, ph2);
  cout << "PRIVN " << priv_n << "\n";
  int d = gen_priv_key (gen_priv_n (ph1, ph2), e);
  cout << "PRIVKEY " << d << "\n";
  int c = encrypt (pub_n, e, message);
  cout << "ENCRYPTED " << c << "\n";
  int data = decrypt (c, d, pub_n);
  cout << "MESSAGE " << data;
}

The PUBN, PRIVN, PRIVKEY, and ENCRYPTED all return the expected numbers, however, the decrypt function should return 89(the message variable), but does not. I'm assuming an arithmetic error in the codes order of operations but I'm not sure. Can the issue be pointed out?

The results of the variables are:

  • PUBN 3127
  • PRIVN 3016
  • PRIVKEY 2011
  • ENCRYPTED 1394
  • MESSAGE -763 <-- unexpected result, should be 89
NovaDev
  • 23
  • 2
  • 5
    Get rid of the non-standard `bits` header, and the `using namespace std;`. There already is a [gcd](https://en.cppreference.com/w/cpp/numeric/gcd) C++ function, and having all of that stuff in the program will make it very easy to have a program that behaves differently than expected. – PaulMcKenzie Jan 21 '22 at 21:46
  • 1
    Looks like `pow(1394, 2011)` is well outside the range of what can be store in an `int`. You'd need to keep applying the modulus as you do the exponent computation to avoid overflow. – Nathan Pierson Jan 21 '22 at 21:46
  • 1
    As to what is wrong, the program, assuming it's supposed to be integer-based, is broken. It is broken because you used a floating point function `pow`, and once you do that, all bets are off as to what outcome the integer-based program will produce. This is even the case if you had smaller values being used in the `pow` function. – PaulMcKenzie Jan 21 '22 at 21:49
  • @NathanPierson How would I do that? with a for loop? – NovaDev Jan 21 '22 at 21:50
  • @PaulMcKenzie is there a better way to raise a number to a power while leaving it as an integer? – NovaDev Jan 21 '22 at 21:51
  • @NovaDev -- Use integer-based techniques. A lookup table, computing by squaring, a "big integer" class that does arbitrary length integer calculations, etc. Basically anything that stays away from floating point approximations. – PaulMcKenzie Jan 21 '22 at 21:52
  • 1
    If you want to play with Cryptography, start learning GNU/GMP – kelalaka Jan 21 '22 at 22:13
  • Welcome to Stack Overflow! Please read the following: [you should never use #include ](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h). – rsjaffe Jan 21 '22 at 22:58

1 Answers1

1

The decrypt (and encrypt) function is broken in that it (a) is exceeding platform representation of int, and (b) is unnecessarily using pow to do it in the first place.

In your trivial example you should be utilizing a technique called modulo chaining . The crux of this is the following.

(a * b) % n == ((a % n) * (b % n)) % n

In your case, you're performing simple exponentiation. That means, for example a^2 % n would be:

(a * a) % n = ((a % n) * (a % n)) % n

Similarly, a^3 % n is:

(a * a * a) % n = (((a % n) * (a % n)) % n) * (a % n)) % n

Using modulo chaining allows you to compute what would otherwise be very large product computation mod some N, so long as no product term pair exceeds your platform representation (and it doesn't in your case).

A trivial version of integer pow that does this is below:

int int_pow_mod(int c, int d, int n)
{
    int res = 1;

    // may as well only do this once
    c %= n;

    // now loop.
    for (int i=0; i<d; ++i)
        res = (res * c) % n;

    return res;
}

Understand, this is no silver bullet. You can easily contrive a c that still produces a product exceeding INT_MAX, but in your case, that won't happen due to your choices of starting material. A general solution will employ this with a big-number library that allows exceeding platform int representation. Regardless, doing that should deliver what you seek, as shown below:

#include <iostream>
using namespace std;

unsigned int gcd(unsigned int a, unsigned int b)
{
    if (a == 0)
        return b;
    return gcd(b % a, a);
}

int phi(unsigned int n)
{
    unsigned int result = 1;
    for (unsigned int i = 2; i < n; i++)
        if (gcd(i, n) == 1)
            result++;
    return result;
}

int gen_priv_n(int p1, int p2)
{
    int n = phi(p1) * phi(p2);
    return n;
}

int gen_pub_n(int p1, int p2)
{
    int n = (p1 * p2);
    return n;
}

int gen_priv_key(int n, int e)
{
    int x = (phi(e) * n + 1) / e;
    return x;
}

int int_pow_mod(int c, int d, int n)
{
    int res = 1;

    // may as well only do this once
    c %= n;

    // now loop.
    for (int i=0; i<d; ++i)
        res = (res * c) % n;

    return res;
}

int encrypt(int n, int e, int data)
{
    int crypt_data = int_pow_mod(data, e, n);
    return crypt_data;
}


int decrypt(int c, int d, int n)
{
    int data = int_pow_mod(c, d, n);
    return data;
}

int main()
{
    int ph1 = 53;
    int ph2 = 59;
    int e = 3;
    int message = 89;

    int pub_n = gen_pub_n(ph1, ph2);
    cout << "PUBN " << pub_n << "\n";
    int priv_n = gen_priv_n(ph1, ph2);
    cout << "PRIVN " << priv_n << "\n";
    int d = gen_priv_key(gen_priv_n(ph1, ph2), e);
    cout << "PRIVKEY " << d << "\n";
    int c = encrypt(pub_n, e, message);
    cout << "ENCRYPTED " << c << "\n";
    int data = decrypt(c, d, pub_n);
    cout << "MESSAGE " << data;
}

Output

PUBN 3127
PRIVN 3016
PRIVKEY 2011
ENCRYPTED 1394
MESSAGE 89
WhozCraig
  • 65,258
  • 11
  • 75
  • 141