-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
/
elliptic_curve_key_exchange.cpp
325 lines (304 loc) · 11.2 KB
/
elliptic_curve_key_exchange.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/**
* @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 <cassert> /// for assert
#include <iostream> /// 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;
}