1 module crypto.rsa;
2 
3 import std.bigint;
4 import std.bitmanip;
5 import std.datetime;
6 import std.base64;
7 import std.typecons;
8 
9 import crypto.bigint;
10 import crypto.random;
11 public import crypto.padding;
12 
13 struct RSAKeyPair
14 {
15     string privateKey;
16     string publicKey;
17 
18     this(string privateKey, string publicKey)
19     {
20         this.privateKey = privateKey;
21         this.publicKey = publicKey;
22     }
23 }
24 
25 struct RSAKeyInfo
26 {
27     @property BigInt modulus()
28     {
29         return _modulus;
30     }
31 
32     @property ubyte[] modulus_bytes()
33     {
34         return _modulus_bytes;
35     }
36 
37     @property BigInt exponent()
38     {
39         return _exponent;
40     }
41 
42     @property ubyte[] exponent_bytes()
43     {
44         return _exponent_bytes;
45     }
46 
47     this(BigInt modulus, ubyte[] modulus_bytes, BigInt exponent, ubyte[] exponent_bytes)
48     {
49         _modulus = modulus;
50         _modulus_bytes = modulus_bytes;
51         _exponent = exponent;
52         _exponent_bytes = exponent_bytes;
53     }
54 
55 private:
56 
57     BigInt _modulus;
58     ubyte[] _modulus_bytes;
59     BigInt _exponent;
60     ubyte[] _exponent_bytes;
61 }
62 
63 class RSA
64 {
65 public:
66 
67     static RSAKeyPair generateKeyPair(uint bitLength = 1024)
68     {
69         assert((bitLength >= 128) && (bitLength % 8 == 0), "Bitlength is required to be a multiple of 8 and not less than 128.");
70 
71         BigInt x, y;
72 
73         BigInt ex_gcd(BigInt a, BigInt b)
74         {
75             if (b == 0)
76             {
77                 x = BigInt("1");
78                 y = BigInt("0");
79                 return a;
80             }
81 
82             BigInt ans = ex_gcd(b, a % b);
83             BigInt temp = x;
84             x = y;
85             y = temp - (a / b * y);
86 
87             return ans;
88         }
89 
90         BigInt cal(BigInt a, BigInt k)
91         {
92             BigInt gcd = ex_gcd(a, k);
93 
94             if (gcd % 1 != 0)
95             {
96                 return BigInt("-1");
97             }
98 
99             x = x * (1 / gcd);
100 
101             if (k < 0)
102             {
103                 k *= -1;
104             }
105 
106             BigInt ans = x % k;
107 
108             if (ans < 0)
109             {
110                 ans = ans += k;
111             }
112 
113             return ans;
114         }
115 
116         BigInt p, q, n, t, e;
117 
118         do
119         {
120             p = BigIntHelper.randomGenerate(bitLength / 2, 1, 1);
121         }
122         while (!BigIntHelper.millerRabinPrimeTest(p, 40));
123         do
124         {
125             q = BigIntHelper.randomGenerate(bitLength / 2, 1, 1);
126         }
127         while (!BigIntHelper.millerRabinPrimeTest(q, 40));
128 
129         n = p * q;
130         t = (p - 1) * (q - 1);
131         do
132         {
133             e = BigIntHelper.randomGenerate(BigInt(60013), BigInt(65537));
134         }
135         while (!BigIntHelper.millerRabinPrimeTest(e, 40));
136 
137         BigInt d = cal(e, t);
138 
139         return RSAKeyPair(encodeKey(n, d), encodeKey(n, e));
140     }
141 
142     static string encodeKey(T : iPKCS = SimpleFormat)(BigInt modulus, BigInt exponent)
143     {
144         return T.encodeKey(modulus, exponent);
145     }
146 
147     static RSAKeyInfo decodeKey(T : iPKCS = SimpleFormat)(string key)
148     {
149         return T.decodeKey(key);
150     }
151 
152     static ubyte[] encrypt(T : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode = false)
153     {
154         return encrypt_decrypt!("encrypt", T)(key, data, mixinXteaMode);
155     }
156 
157     static ubyte[] encrypt(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode = false)
158     {
159         return encrypt_decrypt!"encrypt"(key, data, mixinXteaMode);
160     }
161 
162     static ubyte[] decrypt(T : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode = false)
163     {
164         return encrypt_decrypt!("decrypt", T)(key, data, mixinXteaMode);
165     }
166 
167     static ubyte[] decrypt(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode = false)
168     {
169         return encrypt_decrypt!"decrypt"(key, data, mixinXteaMode);
170     }
171 
172 private:
173 
174     static ubyte[] encrypt_decrypt(string T1, T2 : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode)
175     if (T1 == "encrypt" || T1 == "decrypt")
176     {
177         RSAKeyInfo ki = decodeKey!T2(key);
178         return encrypt_decrypt!(T1)(ki, data, mixinXteaMode);
179     }
180 
181     static ubyte[] encrypt_decrypt(string T)(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode)
182     if (T == "encrypt" || T == "decrypt")
183     {
184         if (mixinXteaMode)
185         {
186             return encrypt_decrypt_mixinXteaMode!T(key, data);
187         }
188         
189         size_t keySize = key.modulus_bytes.length;
190 
191         BigInt getNextBlock(out size_t blockSize)
192         {
193             if (data.length == 0)
194             {
195                 blockSize = 0;
196                 return BigInt("0");
197             }
198 
199             if (T == "decrypt")
200             {
201                 ubyte[] block = data[0 .. ($ >= keySize) ? keySize : $];
202                 blockSize = block.length;
203                 return BigIntHelper.bigIntFromUByteArray(block);
204             }
205             else
206             {
207                 // Prevent preamble 0, and make the encrypto results random
208                 ubyte preamble = rnd.next!ubyte(0x01, 0xFF);
209                 blockSize = (keySize <= data.length) ? keySize : data.length;
210 
211                 while (true)
212                 {
213                     ubyte[] block = [preamble] ~ data[0 .. blockSize];
214                     BigInt t = BigIntHelper.bigIntFromUByteArray(block);
215                     if (t >= key.modulus)
216                     {
217                         blockSize--;
218                         assert(blockSize > 0, "Key bits is too small.");
219                         continue;
220                     }
221                     return t;
222                 }
223             }
224         }
225 
226         ubyte[] ret;
227 
228         while (data.length > 0)
229         {
230             size_t blockSize;
231             BigInt block = getNextBlock(blockSize);
232             if (blockSize == 0)
233             {
234                 break;
235             }
236 
237             block = BigIntHelper.powmod(block, key.exponent, key.modulus);
238             ubyte[] block_buf = BigIntHelper.bigIntToUByteArray(block);
239             if (T == "encrypt")
240             {
241                 for (size_t i; i < keySize - block_buf.length; i++)
242                 {
243                     ret ~= cast(ubyte)0;
244                 }
245             }
246             else
247             {
248                 block_buf = block_buf[1 .. $];
249             }
250 
251             ret ~= block_buf;
252             data = data[blockSize .. $];
253         }
254 
255         return ret;
256     }
257     
258     static ubyte[] encrypt_decrypt_mixinXteaMode(string T)(RSAKeyInfo key, ubyte[] data)
259     if (T == "encrypt" || T == "decrypt")
260     {
261         import crypto.tea;
262 
263         int[4] xteaKey;
264         int rounds = 64;
265         size_t keySize = key.modulus_bytes.length;
266 
267         void generateXteaKey(in ubyte[] buf)
268         {
269             ubyte[] data = new ubyte[int.sizeof * 4];
270             for (int i = 0; i < int.sizeof * 4; i++)
271             {
272                 data[i] = buf[i % buf.length];
273             }
274 
275             for (int i = 0; i < 4; i++)
276             {
277                 xteaKey[i] = data.peek!int(i * int.sizeof);
278             } 
279         }
280 
281         BigInt getNextBlock(out size_t blockSize)
282         {
283             if (data.length == 0)
284             {
285                 blockSize = 0;
286                 return BigInt("0");
287             }
288 
289             if (T == "decrypt")
290             {
291                 ubyte[] block = data[0 .. ($ >= keySize) ? keySize : $];
292                 blockSize = block.length;
293                 return BigIntHelper.bigIntFromUByteArray(block);
294             }
295             else
296             {
297                 // Prevent preamble 0, and make the encrypto results random
298                 ubyte preamble = rnd.next!ubyte(0x01, 0xFF);
299                 blockSize = (keySize <= data.length) ? keySize : data.length;
300 
301                 while (true)
302                 {
303                     ubyte[] block = [preamble] ~ data[0 .. blockSize];
304                     BigInt t = BigIntHelper.bigIntFromUByteArray(block);
305                     if (t >= key.modulus)
306                     {
307                         blockSize--;
308                         assert(blockSize > 0, "Key bits is too small.");
309                         continue;
310                     }
311 
312                     generateXteaKey(block);
313                     return t;
314                 }
315             }
316         }
317 
318         ubyte[] ret;
319 
320         size_t blockSize;
321         BigInt block = getNextBlock(blockSize);
322         if (blockSize == 0)
323         {
324             return ret;
325         }
326 
327         block = BigIntHelper.powmod(block, key.exponent, key.modulus);
328         ubyte[] block_buf = BigIntHelper.bigIntToUByteArray(block);
329         if (T == "encrypt")
330         {
331             for (size_t i; i < keySize - block_buf.length; i++)
332             {
333                 ret ~= cast(ubyte)0;
334             }
335         }
336         else
337         {
338             generateXteaKey(block_buf);
339             block_buf = block_buf[1 .. $];
340         }
341 
342         ret ~= block_buf;
343 
344         if (blockSize >= data.length)
345         {
346             return ret;
347         }
348 
349         data = data[blockSize .. $];
350 
351         if (T == "encrypt")
352         {
353             ret ~= Xtea.encrypt(data, xteaKey, rounds, PaddingMode.Customized);
354         }
355         else
356         {
357             ret ~= Xtea.decrypt(data, xteaKey, rounds, PaddingMode.Customized);
358         }
359 
360         return ret;
361     }
362 }
363 
364 interface iPKCS
365 {
366     static string encodeKey(BigInt modulus, BigInt exponent);
367     static Nullable!RSAKeyInfo decodeKey(string key);
368 }
369 
370 class SimpleFormat : iPKCS
371 {
372     static string encodeKey(BigInt modulus, BigInt exponent)
373     {
374         ubyte[] m_bytes = BigIntHelper.bigIntToUByteArray(modulus);
375         ubyte[] e_bytes = BigIntHelper.bigIntToUByteArray(exponent);
376 
377         ubyte[] buffer = new ubyte[4];
378 
379         buffer.write!int(cast(int) m_bytes.length, 0);
380         buffer ~= m_bytes;
381         buffer ~= e_bytes;
382 
383         return Base64.encode(buffer);
384     }
385 
386     static Nullable!RSAKeyInfo decodeKey(string key)
387     {
388         ubyte[] buffer = Base64.decode(key);
389         int m_len = buffer.peek!int(0);
390         ubyte[] modulus_bytes = buffer[4 .. 4 + m_len];
391         ubyte[] exponent_bytes = buffer[4 + m_len .. $];
392 
393         return Nullable!RSAKeyInfo(RSAKeyInfo(BigIntHelper.bigIntFromUByteArray(modulus_bytes),
394                 modulus_bytes, BigIntHelper.bigIntFromUByteArray(exponent_bytes), exponent_bytes));
395     }
396 }
397 
398 class PKCS1 : iPKCS
399 {
400     static string encodeKey(BigInt modulus, BigInt exponent)
401     {
402         return string.init;
403     }
404 
405     static Nullable!RSAKeyInfo decodeKey(string key)
406     {
407         return Nullable!RSAKeyInfo();
408     }
409 }
410 
411 class PKCS8 : iPKCS
412 {
413     static string encodeKey(BigInt modulus, BigInt exponent)
414     {
415         return string.init;
416     }
417 
418     static Nullable!RSAKeyInfo decodeKey(string key)
419     {
420         return Nullable!RSAKeyInfo();
421     }
422 }
423 
424 unittest
425 {
426     import std.stdio;
427 
428     import crypto.rsa;
429 
430     RSAKeyPair keyPair = RSA.generateKeyPair(1024);
431     writeln(keyPair.privateKey);
432     writeln(keyPair.publicKey);
433 
434     string data = `
435 And the workload proves (POW) reusable workload proof (RPOW) 2. hash function
436 The hash function (Hash Function), also known as a hash function, gives an input x, which calculates the corresponding output H (x). The main features of a hash function are:
437 The input x can be a string of any length
438 The output, that is, the length of H (x) is fixed
439 
440 The procedure for calculating H (x) is efficient (for string X of length n), the time complexity of H (x) should be O (n)
441 For bitcoin, the hash function used by such cryptographic systems, it needs to have the following properties:
442     `;
443 
444     ubyte[] sb = cast(ubyte[]) data;
445     ubyte[] db = RSA.encrypt(keyPair.privateKey, sb);
446     sb = RSA.decrypt(keyPair.publicKey, db);
447     writeln(cast(string) sb);
448 }
449 
450 unittest
451 {
452     import std.stdio;
453 
454     import std.bigint;
455     import crypto.rsa;
456 
457     RSAKeyPair keyPair = RSA.generateKeyPair(1024);
458     writeln(keyPair.privateKey);
459     writeln(keyPair.publicKey);
460 
461     RSAKeyInfo pri_key = RSA.decodeKey(keyPair.privateKey);
462     RSAKeyInfo pub_key = RSA.decodeKey(keyPair.publicKey);
463 
464     string data = `
465 And the workload proves (POW) reusable workload proof (RPOW) 2. hash function
466 The hash function (Hash Function), also known as a hash function, gives an input x, which calculates the corresponding output H (x). The main features of a hash function are:
467 The input x can be a string of any length
468 The output, that is, the length of H (x) is fixed
469 
470 The procedure for calculating H (x) is efficient (for string X of length n), the time complexity of H (x) should be O (n)
471 For bitcoin, the hash function used by such cryptographic systems, it needs to have the following properties:
472     `;
473 
474     ubyte[] sb = cast(ubyte[]) data;
475     ubyte[] db = RSA.encrypt(pri_key, sb);
476     sb = RSA.decrypt(pub_key, db);
477     writeln(cast(string) sb);
478 }