|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @brief Implementation of [Elliptic Curve Diffie Hellman Key |
| 4 | + * Exchange](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange). |
| 5 | + * |
| 6 | + * @details |
| 7 | + * The ECDH (Elliptic Curve Diffie–Hellman Key Exchange) is anonymous key |
| 8 | + * agreement scheme, which allows two parties, each having an elliptic-curve |
| 9 | + * public–private key pair, to establish a shared secret over an insecure |
| 10 | + * channel. |
| 11 | + * ECDH is very similar to the classical DHKE (Diffie–Hellman Key Exchange) |
| 12 | + * algorithm, but it uses ECC point multiplication instead of modular |
| 13 | + * exponentiations. ECDH is based on the following property of EC points: |
| 14 | + * (a * G) * b = (b * G) * a |
| 15 | + * If we have two secret numbers a and b (two private keys, belonging to Alice |
| 16 | + * and Bob) and an ECC elliptic curve with generator point G, we can exchange |
| 17 | + * over an insecure channel the values (a * G) and (b * G) (the public keys of |
| 18 | + * Alice and Bob) and then we can derive a shared secret: |
| 19 | + * secret = (a * G) * b = (b * G) * a. |
| 20 | + * Pretty simple. The above equation takes the following form: |
| 21 | + * alicePubKey * bobPrivKey = bobPubKey * alicePrivKey = secret |
| 22 | + * @author [Ashish Daulatabad](https://github.com/AshishYUO) |
| 23 | + */ |
| 24 | +#include <cassert> /// for assert |
| 25 | +#include <iostream> /// for IO operations |
| 26 | + |
| 27 | +#include "uint256_t.hpp" /// for 256-bit integer |
| 28 | + |
| 29 | +/** |
| 30 | + * @namespace ciphers |
| 31 | + * @brief Cipher algorithms |
| 32 | + */ |
| 33 | +namespace ciphers { |
| 34 | +/** |
| 35 | + * @brief namespace elliptic_curve_key_exchange |
| 36 | + * @details Demonstration of [Elliptic Curve |
| 37 | + * Diffie-Hellman](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange) |
| 38 | + * key exchange. |
| 39 | + */ |
| 40 | +namespace elliptic_curve_key_exchange { |
| 41 | + |
| 42 | +/** |
| 43 | + * @brief Definition of struct Point |
| 44 | + * @details Definition of Point in the curve. |
| 45 | + */ |
| 46 | +typedef struct Point { |
| 47 | + uint256_t x, y; /// x and y co-ordinates |
| 48 | + |
| 49 | + /** |
| 50 | + * @brief operator == for Point |
| 51 | + * @details check whether co-ordinates are equal to the given point |
| 52 | + * @param p given point to be checked with this |
| 53 | + * @returns true if x and y are both equal with Point p, else false |
| 54 | + */ |
| 55 | + inline bool operator==(const Point &p) { return x == p.x && y == p.y; } |
| 56 | + |
| 57 | + /** |
| 58 | + * @brief ostream operator for printing Point |
| 59 | + * @param op ostream operator |
| 60 | + * @param p Point to be printed on console |
| 61 | + * @returns op, the ostream object |
| 62 | + */ |
| 63 | + friend std::ostream &operator<<(std::ostream &op, const Point &p) { |
| 64 | + op << p.x << " " << p.y; |
| 65 | + return op; |
| 66 | + } |
| 67 | +} Point; |
| 68 | + |
| 69 | +/** |
| 70 | + * @brief This function calculates number raised to exponent power under modulo |
| 71 | + * mod using [Modular |
| 72 | + * Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp). |
| 73 | + * @param number integer base |
| 74 | + * @param power unsigned integer exponent |
| 75 | + * @param mod integer modulo |
| 76 | + * @return number raised to power modulo mod |
| 77 | + */ |
| 78 | +uint256_t exp(uint256_t number, uint256_t power, const uint256_t &mod) { |
| 79 | + if (!power) { |
| 80 | + return uint256_t(1); |
| 81 | + } |
| 82 | + uint256_t ans(1); |
| 83 | + number = number % mod; |
| 84 | + while (power) { |
| 85 | + if ((power & 1)) { |
| 86 | + ans = (ans * number) % mod; |
| 87 | + } |
| 88 | + power >>= 1; |
| 89 | + if (power) { |
| 90 | + number = (number * number) % mod; |
| 91 | + } |
| 92 | + } |
| 93 | + return ans; |
| 94 | +} |
| 95 | + |
| 96 | +/** |
| 97 | + * @brief Addition of points |
| 98 | + * @details Add given point to generate third point. More description can be |
| 99 | + * found |
| 100 | + * [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition), |
| 101 | + * and |
| 102 | + * [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_doubling) |
| 103 | + * @param a First point |
| 104 | + * @param b Second point |
| 105 | + * @param curve_a_coeff Coefficient `a` of the given curve (y^2 = x^3 + ax + b) |
| 106 | + * % mod |
| 107 | + * @param mod Given field |
| 108 | + * @return the resultant point |
| 109 | + */ |
| 110 | +Point addition(Point a, Point b, const uint256_t &curve_a_coeff, |
| 111 | + uint256_t mod) { |
| 112 | + uint256_t lambda(0); /// Slope |
| 113 | + uint256_t zero(0); /// value zero |
| 114 | + lambda = zero = 0; |
| 115 | + uint256_t inf = ~zero; |
| 116 | + if (a.x != b.x || a.y != b.y) { |
| 117 | + // Slope being infinite. |
| 118 | + if (b.x == a.x) { |
| 119 | + return {inf, inf}; |
| 120 | + } |
| 121 | + uint256_t num = (b.y - a.y + mod), den = (b.x - a.x + mod); |
| 122 | + lambda = (num * (exp(den, mod - 2, mod))) % mod; |
| 123 | + } else { |
| 124 | + /** |
| 125 | + * slope when the line is tangent to curve. This operation is performed |
| 126 | + * while doubling. Taking derivative of `y^2 = x^3 + ax + b` |
| 127 | + * => `2y dy = (3 * x^2 + a)dx` |
| 128 | + * => `(dy/dx) = (3x^2 + a)/(2y)` |
| 129 | + */ |
| 130 | + /** |
| 131 | + * if y co-ordinate is zero, the slope is infinite, return inf. |
| 132 | + * else calculate the slope (here % mod and store in lambda) |
| 133 | + */ |
| 134 | + if (!a.y) { |
| 135 | + return {inf, inf}; |
| 136 | + } |
| 137 | + uint256_t axsq = ((a.x * a.x)) % mod; |
| 138 | + // Mulitply by 3 adjustment |
| 139 | + axsq += (axsq << 1); |
| 140 | + axsq %= mod; |
| 141 | + // Mulitply by 2 adjustment |
| 142 | + uint256_t a_2 = (a.y << 1); |
| 143 | + lambda = |
| 144 | + (((axsq + curve_a_coeff) % mod) * exp(a_2, mod - 2, mod)) % mod; |
| 145 | + } |
| 146 | + Point c; |
| 147 | + // new point: x = ((lambda^2) - x1 - x2) |
| 148 | + // y = (lambda * (x1 - x)) - y1 |
| 149 | + c.x = ((lambda * lambda) % mod + (mod << 1) - a.x - b.x) % mod; |
| 150 | + c.y = (((lambda * (a.x + mod - c.x)) % mod) + mod - a.y) % mod; |
| 151 | + return c; |
| 152 | +} |
| 153 | + |
| 154 | +/** |
| 155 | + * @brief multiply Point and integer |
| 156 | + * @details Multiply Point by a scalar factor (here it is a private key p). The |
| 157 | + * multiplication is called as [double and add |
| 158 | + * method](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add) |
| 159 | + * @param a Point to multiply |
| 160 | + * @param curve_a_coeff Coefficient of given curve (y^2 = x^3 + ax + b) % mod |
| 161 | + * @param p The scalar value |
| 162 | + * @param mod Given field |
| 163 | + * @returns the resultant point |
| 164 | + */ |
| 165 | +Point multiply(const Point &a, const uint256_t &curve_a_coeff, uint256_t p, |
| 166 | + const uint256_t &mod) { |
| 167 | + Point N = a; |
| 168 | + N.x %= mod; |
| 169 | + N.y %= mod; |
| 170 | + uint256_t inf{}; |
| 171 | + inf = ~uint256_t(0); |
| 172 | + Point Q = {inf, inf}; |
| 173 | + while (p) { |
| 174 | + if ((p & 1)) { |
| 175 | + if (Q.x == inf && Q.y == inf) { |
| 176 | + Q.x = N.x; |
| 177 | + Q.y = N.y; |
| 178 | + } else { |
| 179 | + Q = addition(Q, N, curve_a_coeff, mod); |
| 180 | + } |
| 181 | + } |
| 182 | + p >>= 1; |
| 183 | + if (p) { |
| 184 | + N = addition(N, N, curve_a_coeff, mod); |
| 185 | + } |
| 186 | + } |
| 187 | + return Q; |
| 188 | +} |
| 189 | +} // namespace elliptic_curve_key_exchange |
| 190 | +} // namespace ciphers |
| 191 | + |
| 192 | +/** |
| 193 | + * @brief Function to test the |
| 194 | + * uint128_t header |
| 195 | + * @returns void |
| 196 | + */ |
| 197 | +static void uint128_t_tests() { |
| 198 | + // 1st test: Operations test |
| 199 | + uint128_t a("122"), b("2312"); |
| 200 | + assert(a + b == 2434); |
| 201 | + assert(b - a == 2190); |
| 202 | + assert(a * b == 282064); |
| 203 | + assert(b / a == 18); |
| 204 | + assert(b % a == 116); |
| 205 | + assert((a & b) == 8); |
| 206 | + assert((a | b) == 2426); |
| 207 | + assert((a ^ b) == 2418); |
| 208 | + assert((a << 64) == uint128_t("2250502776992565297152")); |
| 209 | + assert((b >> 7) == 18); |
| 210 | + |
| 211 | + // 2nd test: Operations test |
| 212 | + a = uint128_t("12321421424232142122"); |
| 213 | + b = uint128_t("23123212"); |
| 214 | + assert(a + b == uint128_t("12321421424255265334")); |
| 215 | + assert(a - b == uint128_t("12321421424209018910")); |
| 216 | + assert(a * b == uint128_t("284910839733861759501135864")); |
| 217 | + assert(a / b == 532859423865LL); |
| 218 | + assert(a % b == 3887742); |
| 219 | + assert((a & b) == 18912520); |
| 220 | + assert((a | b) == uint128_t("12321421424236352814")); |
| 221 | + assert((a ^ b) == uint128_t("12321421424217440294")); |
| 222 | + assert((a << 64) == uint128_t("227290107637132170748078080907806769152")); |
| 223 | +} |
| 224 | + |
| 225 | +/** |
| 226 | + * @brief Function to test the |
| 227 | + * uint256_t header |
| 228 | + * @returns void |
| 229 | + */ |
| 230 | +static void uint256_t_tests() { |
| 231 | + // 1st test: Operations test |
| 232 | + uint256_t a("122"), b("2312"); |
| 233 | + assert(a + b == 2434); |
| 234 | + assert(b - a == 2190); |
| 235 | + assert(a * b == 282064); |
| 236 | + assert(b / a == 18); |
| 237 | + assert(b % a == 116); |
| 238 | + assert((a & b) == 8); |
| 239 | + assert((a | b) == 2426); |
| 240 | + assert((a ^ b) == 2418); |
| 241 | + assert((a << 64) == uint256_t("2250502776992565297152")); |
| 242 | + assert((b >> 7) == 18); |
| 243 | + |
| 244 | + // 2nd test: Operations test |
| 245 | + a = uint256_t("12321423124513251424232142122"); |
| 246 | + b = uint256_t("23124312431243243215354315132413213212"); |
| 247 | + assert(a + b == uint256_t("23124312443564666339867566556645355334")); |
| 248 | + // Since a < b, the value is greater |
| 249 | + assert(a - b == uint256_t("115792089237316195423570985008687907853246860353" |
| 250 | + "221642219366742944204948568846")); |
| 251 | + assert(a * b == uint256_t("284924437928789743312147393953938013677909398222" |
| 252 | + "169728183872115864")); |
| 253 | + assert(b / a == uint256_t("1876756621")); |
| 254 | + assert(b % a == uint256_t("2170491202688962563936723450")); |
| 255 | + assert((a & b) == uint256_t("3553901085693256462344")); |
| 256 | + assert((a | b) == uint256_t("23124312443564662785966480863388892990")); |
| 257 | + assert((a ^ b) == uint256_t("23124312443564659232065395170132430646")); |
| 258 | + assert((a << 128) == uint256_t("4192763024643754272961909047609369343091683" |
| 259 | + "376561852756163540549632")); |
| 260 | +} |
| 261 | + |
| 262 | +/** |
| 263 | + * @brief Function to test the |
| 264 | + * provided algorithm above |
| 265 | + * @returns void |
| 266 | + */ |
| 267 | +static void test() { |
| 268 | + // demonstration of key exchange using curve secp112r1 |
| 269 | + |
| 270 | + // Equation of the form y^2 = (x^3 + ax + b) % P (here p is mod) |
| 271 | + uint256_t a("4451685225093714772084598273548424"), |
| 272 | + b("2061118396808653202902996166388514"), |
| 273 | + mod("4451685225093714772084598273548427"); |
| 274 | + |
| 275 | + // Generator value: is pre-defined for the given curve |
| 276 | + ciphers::elliptic_curve_key_exchange::Point ptr = { |
| 277 | + uint256_t("188281465057972534892223778713752"), |
| 278 | + uint256_t("3419875491033170827167861896082688")}; |
| 279 | + |
| 280 | + // Shared key generation. |
| 281 | + // For alice |
| 282 | + std::cout << "For alice:\n"; |
| 283 | + // Alice's private key (can be generated randomly) |
| 284 | + uint256_t alice_private_key("164330438812053169644452143505618"); |
| 285 | + ciphers::elliptic_curve_key_exchange::Point alice_public_key = |
| 286 | + multiply(ptr, a, alice_private_key, mod); |
| 287 | + std::cout << "\tPrivate key: " << alice_private_key << "\n"; |
| 288 | + std::cout << "\tPublic Key: " << alice_public_key << std::endl; |
| 289 | + |
| 290 | + // For Bob |
| 291 | + std::cout << "For Bob:\n"; |
| 292 | + // Bob's private key (can be generated randomly) |
| 293 | + uint256_t bob_private_key("1959473333748537081510525763478373"); |
| 294 | + ciphers::elliptic_curve_key_exchange::Point bob_public_key = |
| 295 | + multiply(ptr, a, bob_private_key, mod); |
| 296 | + std::cout << "\tPrivate key: " << bob_private_key << "\n"; |
| 297 | + std::cout << "\tPublic Key: " << bob_public_key << std::endl; |
| 298 | + |
| 299 | + // After public key exchange, create a shared key for communication. |
| 300 | + // create shared key: |
| 301 | + ciphers::elliptic_curve_key_exchange::Point alice_shared_key = multiply( |
| 302 | + bob_public_key, a, |
| 303 | + alice_private_key, mod), |
| 304 | + bob_shared_key = multiply( |
| 305 | + alice_public_key, a, |
| 306 | + bob_private_key, mod); |
| 307 | + |
| 308 | + std::cout << "Shared keys:\n"; |
| 309 | + std::cout << alice_shared_key << std::endl; |
| 310 | + std::cout << bob_shared_key << std::endl; |
| 311 | + |
| 312 | + // Check whether shared keys are equal |
| 313 | + assert(alice_shared_key == bob_shared_key); |
| 314 | +} |
| 315 | + |
| 316 | +/** |
| 317 | + * @brief Main function |
| 318 | + * @returns 0 on exit |
| 319 | + */ |
| 320 | +int main() { |
| 321 | + uint128_t_tests(); // running predefined 128-bit unsigned integer tests |
| 322 | + uint256_t_tests(); // running predefined 256-bit unsigned integer tests |
| 323 | + test(); // running self-test implementations |
| 324 | + return 0; |
| 325 | +} |
0 commit comments