/** * @file * @brief Implementation of [Elliptic Curve Diffie Hellman Key * Exchange](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange). * * @details * The ECDH (Elliptic Curve Diffie–Hellman Key Exchange) is anonymous key * agreement scheme, which allows two parties, each having an elliptic-curve * public–private key pair, to establish a shared secret over an insecure * channel. * ECDH is very similar to the classical DHKE (Diffie–Hellman Key Exchange) * algorithm, but it uses ECC point multiplication instead of modular * exponentiations. ECDH is based on the following property of EC points: * (a * G) * b = (b * G) * a * If we have two secret numbers a and b (two private keys, belonging to Alice * and Bob) and an ECC elliptic curve with generator point G, we can exchange * over an insecure channel the values (a * G) and (b * G) (the public keys of * Alice and Bob) and then we can derive a shared secret: * secret = (a * G) * b = (b * G) * a. * Pretty simple. The above equation takes the following form: * alicePubKey * bobPrivKey = bobPubKey * alicePrivKey = secret * @author [Ashish Daulatabad](https://github.com/AshishYUO) */ #include /// for assert #include /// for IO operations #include "uint256_t.hpp" /// for 256-bit integer /** * @namespace ciphers * @brief Cipher algorithms */ namespace ciphers { /** * @brief namespace elliptic_curve_key_exchange * @details Demonstration of [Elliptic Curve * Diffie-Hellman](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange) * key exchange. */ namespace elliptic_curve_key_exchange { /** * @brief Definition of struct Point * @details Definition of Point in the curve. */ typedef struct Point { uint256_t x, y; /// x and y co-ordinates /** * @brief operator == for Point * @details check whether co-ordinates are equal to the given point * @param p given point to be checked with this * @returns true if x and y are both equal with Point p, else false */ inline bool operator==(const Point &p) { return x == p.x && y == p.y; } /** * @brief ostream operator for printing Point * @param op ostream operator * @param p Point to be printed on console * @returns op, the ostream object */ friend std::ostream &operator<<(std::ostream &op, const Point &p) { op << p.x << " " << p.y; return op; } } Point; /** * @brief This function calculates number raised to exponent power under modulo * mod using [Modular * Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp). * @param number integer base * @param power unsigned integer exponent * @param mod integer modulo * @return number raised to power modulo mod */ uint256_t exp(uint256_t number, uint256_t power, const uint256_t &mod) { if (!power) { return uint256_t(1); } uint256_t ans(1); number = number % mod; while (power) { if ((power & 1)) { ans = (ans * number) % mod; } power >>= 1; if (power) { number = (number * number) % mod; } } return ans; } /** * @brief Addition of points * @details Add given point to generate third point. More description can be * found * [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition), * and * [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_doubling) * @param a First point * @param b Second point * @param curve_a_coeff Coefficient `a` of the given curve (y^2 = x^3 + ax + b) * % mod * @param mod Given field * @return the resultant point */ Point addition(Point a, Point b, const uint256_t &curve_a_coeff, uint256_t mod) { uint256_t lambda(0); /// Slope uint256_t zero(0); /// value zero lambda = zero = 0; uint256_t inf = ~zero; if (a.x != b.x || a.y != b.y) { // Slope being infinite. if (b.x == a.x) { return {inf, inf}; } uint256_t num = (b.y - a.y + mod), den = (b.x - a.x + mod); lambda = (num * (exp(den, mod - 2, mod))) % mod; } else { /** * slope when the line is tangent to curve. This operation is performed * while doubling. Taking derivative of `y^2 = x^3 + ax + b` * => `2y dy = (3 * x^2 + a)dx` * => `(dy/dx) = (3x^2 + a)/(2y)` */ /** * if y co-ordinate is zero, the slope is infinite, return inf. * else calculate the slope (here % mod and store in lambda) */ if (!a.y) { return {inf, inf}; } uint256_t axsq = ((a.x * a.x)) % mod; // Mulitply by 3 adjustment axsq += (axsq << 1); axsq %= mod; // Mulitply by 2 adjustment uint256_t a_2 = (a.y << 1); lambda = (((axsq + curve_a_coeff) % mod) * exp(a_2, mod - 2, mod)) % mod; } Point c; // new point: x = ((lambda^2) - x1 - x2) // y = (lambda * (x1 - x)) - y1 c.x = ((lambda * lambda) % mod + (mod << 1) - a.x - b.x) % mod; c.y = (((lambda * (a.x + mod - c.x)) % mod) + mod - a.y) % mod; return c; } /** * @brief multiply Point and integer * @details Multiply Point by a scalar factor (here it is a private key p). The * multiplication is called as [double and add * method](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add) * @param a Point to multiply * @param curve_a_coeff Coefficient of given curve (y^2 = x^3 + ax + b) % mod * @param p The scalar value * @param mod Given field * @returns the resultant point */ Point multiply(const Point &a, const uint256_t &curve_a_coeff, uint256_t p, const uint256_t &mod) { Point N = a; N.x %= mod; N.y %= mod; uint256_t inf{}; inf = ~uint256_t(0); Point Q = {inf, inf}; while (p) { if ((p & 1)) { if (Q.x == inf && Q.y == inf) { Q.x = N.x; Q.y = N.y; } else { Q = addition(Q, N, curve_a_coeff, mod); } } p >>= 1; if (p) { N = addition(N, N, curve_a_coeff, mod); } } return Q; } } // namespace elliptic_curve_key_exchange } // namespace ciphers /** * @brief Function to test the * uint128_t header * @returns void */ static void uint128_t_tests() { // 1st test: Operations test uint128_t a("122"), b("2312"); assert(a + b == 2434); assert(b - a == 2190); assert(a * b == 282064); assert(b / a == 18); assert(b % a == 116); assert((a & b) == 8); assert((a | b) == 2426); assert((a ^ b) == 2418); assert((a << 64) == uint128_t("2250502776992565297152")); assert((b >> 7) == 18); // 2nd test: Operations test a = uint128_t("12321421424232142122"); b = uint128_t("23123212"); assert(a + b == uint128_t("12321421424255265334")); assert(a - b == uint128_t("12321421424209018910")); assert(a * b == uint128_t("284910839733861759501135864")); assert(a / b == 532859423865LL); assert(a % b == 3887742); assert((a & b) == 18912520); assert((a | b) == uint128_t("12321421424236352814")); assert((a ^ b) == uint128_t("12321421424217440294")); assert((a << 64) == uint128_t("227290107637132170748078080907806769152")); } /** * @brief Function to test the * uint256_t header * @returns void */ static void uint256_t_tests() { // 1st test: Operations test uint256_t a("122"), b("2312"); assert(a + b == 2434); assert(b - a == 2190); assert(a * b == 282064); assert(b / a == 18); assert(b % a == 116); assert((a & b) == 8); assert((a | b) == 2426); assert((a ^ b) == 2418); assert((a << 64) == uint256_t("2250502776992565297152")); assert((b >> 7) == 18); // 2nd test: Operations test a = uint256_t("12321423124513251424232142122"); b = uint256_t("23124312431243243215354315132413213212"); assert(a + b == uint256_t("23124312443564666339867566556645355334")); // Since a < b, the value is greater assert(a - b == uint256_t("115792089237316195423570985008687907853246860353" "221642219366742944204948568846")); assert(a * b == uint256_t("284924437928789743312147393953938013677909398222" "169728183872115864")); assert(b / a == uint256_t("1876756621")); assert(b % a == uint256_t("2170491202688962563936723450")); assert((a & b) == uint256_t("3553901085693256462344")); assert((a | b) == uint256_t("23124312443564662785966480863388892990")); assert((a ^ b) == uint256_t("23124312443564659232065395170132430646")); assert((a << 128) == uint256_t("4192763024643754272961909047609369343091683" "376561852756163540549632")); } /** * @brief Function to test the * provided algorithm above * @returns void */ static void test() { // demonstration of key exchange using curve secp112r1 // Equation of the form y^2 = (x^3 + ax + b) % P (here p is mod) uint256_t a("4451685225093714772084598273548424"), b("2061118396808653202902996166388514"), mod("4451685225093714772084598273548427"); // Generator value: is pre-defined for the given curve ciphers::elliptic_curve_key_exchange::Point ptr = { uint256_t("188281465057972534892223778713752"), uint256_t("3419875491033170827167861896082688")}; // Shared key generation. // For alice std::cout << "For alice:\n"; // Alice's private key (can be generated randomly) uint256_t alice_private_key("164330438812053169644452143505618"); ciphers::elliptic_curve_key_exchange::Point alice_public_key = multiply(ptr, a, alice_private_key, mod); std::cout << "\tPrivate key: " << alice_private_key << "\n"; std::cout << "\tPublic Key: " << alice_public_key << std::endl; // For Bob std::cout << "For Bob:\n"; // Bob's private key (can be generated randomly) uint256_t bob_private_key("1959473333748537081510525763478373"); ciphers::elliptic_curve_key_exchange::Point bob_public_key = multiply(ptr, a, bob_private_key, mod); std::cout << "\tPrivate key: " << bob_private_key << "\n"; std::cout << "\tPublic Key: " << bob_public_key << std::endl; // After public key exchange, create a shared key for communication. // create shared key: ciphers::elliptic_curve_key_exchange::Point alice_shared_key = multiply( bob_public_key, a, alice_private_key, mod), bob_shared_key = multiply( alice_public_key, a, bob_private_key, mod); std::cout << "Shared keys:\n"; std::cout << alice_shared_key << std::endl; std::cout << bob_shared_key << std::endl; // Check whether shared keys are equal assert(alice_shared_key == bob_shared_key); } /** * @brief Main function * @returns 0 on exit */ int main() { uint128_t_tests(); // running predefined 128-bit unsigned integer tests uint256_t_tests(); // running predefined 256-bit unsigned integer tests test(); // running self-test implementations return 0; }