#include <iostream>
#include <NTL/ZZ.h>
#include <NTL/ZZ_p.h>
using namespace std;
using namespace NTL;
// Define the elliptic curve parameters for NIST P-192
const char* PRIME_P =
"6277101735386680763835789423207666416083908700390324961279";
const char* A = "-3";
const char* B =
"2455155546008943817740293915197451784769108058161191238065";
const char* GX =
"602046282375688656758213480587526111916698976636884684818";
const char* GY =
"174050332293622031404857552280219410364023488927386650641";
const char* N =
"6277101735386680763835789423176059013767194773182842284081";
struct Point {
ZZ_p x, y;
bool is_infinity = false;
Point() : is_infinity(true) {}
Point(ZZ_p x, ZZ_p y) : x(x), y(y), is_infinity(false) {}
};
// Function to add two points on the curve
Point point_add(const Point& P, const Point& Q, const ZZ_p& a) {
if (P.is_infinity) return Q;
if (Q.is_infinity) return P;
if (P.x == Q.x && P.y == -Q.y) return Point();
ZZ_p m;
if (P.x == Q.x && P.y == Q.y) {
m = (3 * sqr(P.x) + a) / (2 * P.y);
} else {
m = (Q.y - P.y) / (Q.x - P.x);
}
ZZ_p rx = sqr(m) - P.x - Q.x;
ZZ_p ry = m * (P.x - rx) - P.y;
return Point(rx, ry);
}
// Scalar multiplication: k * P
Point scalar_mult(const ZZ& k, const Point& P, const ZZ_p& a) {
Point result; // Infinity
Point current = P;
ZZ n = k;
while (n > 0) {
if (n % 2 == 1) {
result = point_add(result, current, a);
}
current = point_add(current, current, a);
n /= 2;
}
return result;
}
int main() {
// Initialize finite field
ZZ p;
conv(p, PRIME_P);
ZZ_p::init(p);
// Define curve parameters
ZZ_p a, b;
conv(a, A);
conv(b, B);
// Base point G
ZZ_p gx, gy;
conv(gx, GX);
conv(gy, GY);
Point G(gx, gy);
// Order of the curve
ZZ n;
conv(n, N);
// Generate receiver's private key and public key
ZZ private_key = RandomBnd(n); // Receiver's private key
Point public_key = scalar_mult(private_key, G, a); // Receiver's
public key
// Plaintext (message as a point on the curve)
Point M(ZZ_p(12345), ZZ_p(67890)); // Example point (M must be
on the curve)
// Sender generates a random ephemeral key
ZZ k = RandomBnd(n);
Point C1 = scalar_mult(k, G, a);
Point C2 = point_add(M, scalar_mult(k, public_key, a), a);
// Ciphertext: (C1, C2)
cout << "Ciphertext C1: (" << C1.x << ", " << C1.y << ")" <<
endl;
cout << "Ciphertext C2: (" << C2.x << ", " << C2.y << ")" <<
endl;
// Decryption
Point kQ = scalar_mult(private_key, C1, a);
Point decrypted_message = point_add(C2, Point(kQ.x, -kQ.y), a);
cout << "Decrypted Message: (" << decrypted_message.x << ", " <<
decrypted_message.y << ")" << endl;
return 0;
}