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 }