Last active
December 17, 2017 22:41
-
-
Save akosma/05b42ced007af375684906f5320a2366 to your computer and use it in GitHub Desktop.
RSA using the C++ interface of the GMP library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
CmakeLists.txt: | |
cmake_minimum_required(VERSION 3.6) | |
project(rsa) | |
set(CMAKE_CXX_STANDARD 14) | |
set(SOURCE_FILES main.cpp) | |
add_executable(rsa ${SOURCE_FILES}) | |
target_link_libraries(rsa gmpxx gmp) | |
*/ | |
#include <iostream> | |
#include <gmpxx.h> | |
#include <assert.h> | |
#include <map> | |
using std::cout; | |
using std::endl; | |
using std::pair; | |
using std::map; | |
using std::make_pair; | |
using std::string; | |
using keyset = map<string, pair<mpz_class, mpz_class>>; | |
// Calculation of the RSA keyset using GMP | |
// https://gmplib.org/ | |
// Based on an earlier version in PHP | |
// https://gist.github.com/akosma/9058c43c76da2e6691637b1332058ddc | |
keyset rsa_keys(const mpz_t p, const mpz_t q, const mpz_t e) { | |
mpz_t d, lambda, gcd; | |
mpz_inits(d, lambda, gcd, NULL); | |
mpz_class pc{p}; | |
mpz_class qc{q}; | |
mpz_class nc = pc * qc; | |
mpz_class pc_1 = pc - 1; | |
mpz_class qc_1 = qc - 1; | |
mpz_lcm(lambda, pc_1.get_mpz_t(), qc_1.get_mpz_t()); | |
mpz_class lambdac{lambda}; | |
cout << "lambda = " << lambdac << endl; | |
// e must be bigger than 1 | |
mpz_class ec{e}; | |
assert(ec > 1); | |
// e must be smaller than lambda | |
assert(ec < lambdac); | |
// GCD(e, lambda) must be 1 | |
mpz_gcd(gcd, e, lambda); | |
mpz_class gcdc{gcd}; | |
assert(gcdc == 1); | |
mpz_invert(d, e, lambda); | |
mpz_class dc{d}; | |
// e * d MOD lambda must be 1 | |
mpz_class calc = ec * dc % lambdac; | |
assert(calc == 1); | |
keyset result{{"public", make_pair(ec, nc)}, | |
{"private", make_pair(dc, nc)}}; | |
mpz_clears(d, gcd, lambda, NULL); | |
return result; | |
} | |
// RSA encryption | |
mpz_class encrypt(const mpz_t message, | |
const mpz_t e, | |
const mpz_t n) { | |
mpz_t encrypted; | |
mpz_init(encrypted); | |
mpz_powm(encrypted, message, e, n); | |
mpz_class result{encrypted}; | |
mpz_clear(encrypted); | |
return result; | |
} | |
// RSA decryption | |
mpz_class decrypt(const mpz_t encrypted, | |
const mpz_t d, | |
const mpz_t n) { | |
mpz_t original; | |
mpz_init(original); | |
mpz_powm(original, encrypted, d, n); | |
mpz_class result{original}; | |
mpz_clear(original); | |
return result; | |
} | |
void display(mpz_class message, keyset k) { | |
mpz_class e = k["public"].first; | |
mpz_class d = k["private"].first; | |
mpz_class n = k["public"].second; | |
mpz_class encrypted = encrypt(message.get_mpz_t(), e.get_mpz_t(), n.get_mpz_t()); | |
mpz_class decrypted = decrypt(encrypted.get_mpz_t(), d.get_mpz_t(), n.get_mpz_t()); | |
// The decrypted message must be equal to the original | |
assert(message == decrypted); | |
cout << "Public key = (e: " << e << ", n: " << n << ")" << endl; | |
cout << "Private key = (d: " << d << ", n: " << n << ")" << endl; | |
cout << "Original message: " << message << endl; | |
cout << "Encrypted message: " << encrypted << endl; | |
cout << "Decrypted message: " << decrypted << endl; | |
cout << endl; | |
} | |
template<typename T> | |
void display(T msg, T pi, T qi, T ei) { | |
cout << "Initializing with p = " << pi << ", q = " << qi << ", e = " << ei << endl; | |
mpz_t n, d; | |
mpz_inits(n, d, NULL); | |
mpz_class p{pi}; | |
mpz_class q{qi}; | |
mpz_class e{ei}; | |
mpz_class original{msg}; | |
auto k = rsa_keys(p.get_mpz_t(), q.get_mpz_t(), e.get_mpz_t()); | |
display(original, k); | |
mpz_clears(n, d, NULL); | |
} | |
int main() { | |
// Example taken from Wikipedia | |
// https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation | |
display(65, 61, 53, 17); | |
// Example from Twitter | |
// https://twitter.com/kosamari/status/838738015010848769 | |
mpz_class msg{123}; | |
mpz_class n{323}; | |
mpz_class e{5}; | |
mpz_class d{29}; | |
keyset k{{"public", make_pair(e, n)}, | |
{"private", make_pair(d, n)}}; | |
display(msg, k); | |
// Very small prime numbers | |
display(123, 13, 19, 17); | |
// With some prime numbers from | |
// http://www.bigprimes.net/ | |
display(67890, 541, 461, 107); | |
display(123456, 1181, 929, 173); | |
// The PHP version takes around 10 seconds on a MacBook Air | |
display(123456, 1181, 929, 1987); | |
// The PHP takes around 40 seconds in a MacBook Air | |
display(123456, 1181, 929, 17); | |
// Very big numbers; using Mersenne primes, | |
// something impossible in the PHP version | |
display("1111119999999999911111111", | |
"162259276829213363391578010288127", | |
"618970019642690137449562111", | |
"170141183460469231731687303715884105727"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @selango2017 I have just tried this and it did work with me.
Be sure to have copied the CMakeList or, otherwise, compile with -lgmpxx -lgmp options.