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 = 2048) 68 { 69 assert((bitLength >= 128) && (bitLength % 8 == 0), 70 "Bitlength is required to be a multiple of 8 and not less than 128. It’s recommended that it be no less than 2048."); 71 72 BigInt x, y; 73 74 BigInt ex_gcd(BigInt a, BigInt b) 75 { 76 if (b == 0) 77 { 78 x = BigInt("1"); 79 y = BigInt("0"); 80 return a; 81 } 82 83 BigInt ans = ex_gcd(b, a % b); 84 BigInt temp = x; 85 x = y; 86 y = temp - (a / b * y); 87 88 return ans; 89 } 90 91 BigInt cal(BigInt a, BigInt k) 92 { 93 BigInt gcd = ex_gcd(a, k); 94 95 if (gcd % 1 != 0) 96 { 97 return BigInt("-1"); 98 } 99 100 x = x * (1 / gcd); 101 102 if (k < 0) 103 { 104 k *= -1; 105 } 106 107 BigInt ans = x % k; 108 109 if (ans < 0) 110 { 111 ans += k; 112 } 113 114 return ans; 115 } 116 117 size_t confidence; 118 if (bitLength <= 128) confidence = 50; 119 else if (bitLength <= 256) confidence = 27; 120 else if (bitLength <= 512) confidence = 15; 121 else if (bitLength <= 768) confidence = 8; 122 else if (bitLength <= 1024) confidence = 4; 123 else confidence = 2; 124 125 BigInt p, q, n, t, e, d; 126 127 do 128 { 129 p = BigIntHelper.randomGenerate(bitLength / 2, 1, 1); 130 } 131 while (!BigIntHelper.isProbablePrime(p, confidence)); 132 do 133 { 134 q = BigIntHelper.randomGenerate(bitLength / 2, 1, 1); 135 } 136 while (!BigIntHelper.isProbablePrime(q, confidence)); 137 138 n = p * q; 139 t = (p - 1) * (q - 1); 140 e = 65537; 141 d = cal(e, t); 142 143 return RSAKeyPair(encodeKey(n, d), encodeKey(n, e)); 144 } 145 146 static string encodeKey(T : iPKCS = SimpleFormat)(BigInt modulus, BigInt exponent) 147 { 148 return T.encodeKey(modulus, exponent); 149 } 150 151 static RSAKeyInfo decodeKey(T : iPKCS = SimpleFormat)(string key) 152 { 153 return T.decodeKey(key).get; 154 } 155 156 static ubyte[] encrypt(T : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode = false) 157 { 158 return crypt!("encrypt", T)(key, data, mixinXteaMode); 159 } 160 161 static ubyte[] encrypt(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode = false) 162 { 163 return crypt!"encrypt"(key, data, mixinXteaMode); 164 } 165 166 static ubyte[] decrypt(T : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode = false) 167 { 168 return crypt!("decrypt", T)(key, data, mixinXteaMode); 169 } 170 171 static ubyte[] decrypt(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode = false) 172 { 173 return crypt!"decrypt"(key, data, mixinXteaMode); 174 } 175 176 private: 177 178 static ubyte[] crypt(string T1, T2 : iPKCS = SimpleFormat)(string key, ubyte[] data, bool mixinXteaMode) 179 if (T1 == "encrypt" || T1 == "decrypt") 180 { 181 RSAKeyInfo ki = decodeKey!T2(key); 182 return crypt!(T1)(ki, data, mixinXteaMode); 183 } 184 185 static ubyte[] crypt(string T)(RSAKeyInfo key, ubyte[] data, bool mixinXteaMode) 186 if (T == "encrypt" || T == "decrypt") 187 { 188 if (mixinXteaMode) 189 { 190 return crypt_mixinXteaMode!T(key, data); 191 } 192 193 size_t keySize = key.modulus_bytes.length; 194 195 BigInt getNextBlock(out size_t blockSize) 196 { 197 if (data.length == 0) 198 { 199 blockSize = 0; 200 return BigInt("0"); 201 } 202 203 static if (T == "decrypt") 204 { 205 ubyte[] block = data[0 .. ($ >= keySize) ? keySize : $]; 206 blockSize = block.length; 207 return BigIntHelper.fromBytes(block); 208 } 209 else 210 { 211 // Prevent preamble 0, and make the encrypto results random 212 ubyte preamble = rnd.next!ubyte(0x01, 0xFF); 213 blockSize = (keySize <= data.length) ? keySize : data.length; 214 215 while (true) 216 { 217 ubyte[] block = [preamble] ~ data[0 .. blockSize]; 218 BigInt t = BigIntHelper.fromBytes(block); 219 if (t >= key.modulus) 220 { 221 blockSize--; 222 assert(blockSize > 0, "Key bits is too small."); 223 continue; 224 } 225 return t; 226 } 227 } 228 } 229 230 ubyte[] ret; 231 232 while (data.length > 0) 233 { 234 size_t blockSize; 235 BigInt block = getNextBlock(blockSize); 236 if (blockSize == 0) 237 { 238 break; 239 } 240 241 block = BigIntHelper.powmod(block, key.exponent, key.modulus); 242 ubyte[] block_buf = BigIntHelper.toUBytes(block); 243 static if (T == "encrypt") 244 { 245 for (size_t i; i < keySize - block_buf.length; i++) 246 { 247 ret ~= cast(ubyte)0; 248 } 249 } 250 else 251 { 252 block_buf = block_buf[1 .. $]; 253 } 254 255 ret ~= block_buf; 256 data = data[blockSize .. $]; 257 } 258 259 return ret; 260 } 261 262 static ubyte[] crypt_mixinXteaMode(string T)(RSAKeyInfo key, ubyte[] data) 263 if (T == "encrypt" || T == "decrypt") 264 { 265 import crypto.tea; 266 267 int[4] xteaKey; 268 int rounds = 64; 269 size_t keySize = key.modulus_bytes.length; 270 271 void generateXteaKey(in ubyte[] buf) 272 { 273 ubyte[] data = new ubyte[int.sizeof * 4]; 274 for (int i = 0; i < int.sizeof * 4; i++) 275 { 276 data[i] = buf[i % buf.length]; 277 } 278 279 for (int i = 0; i < 4; i++) 280 { 281 xteaKey[i] = data.peek!int(i * int.sizeof); 282 } 283 } 284 285 BigInt getNextBlock(out size_t blockSize) 286 { 287 if (data.length == 0) 288 { 289 blockSize = 0; 290 return BigInt("0"); 291 } 292 293 static if (T == "decrypt") 294 { 295 ubyte[] block = data[0 .. ($ >= keySize) ? keySize : $]; 296 blockSize = block.length; 297 return BigIntHelper.fromBytes(block); 298 } 299 else 300 { 301 // Prevent preamble 0, and make the encrypto results random 302 ubyte preamble = rnd.next!ubyte(0x01, 0xFF); 303 blockSize = (keySize <= data.length) ? keySize : data.length; 304 305 while (true) 306 { 307 ubyte[] block = [preamble] ~ data[0 .. blockSize]; 308 BigInt t = BigIntHelper.fromBytes(block); 309 if (t >= key.modulus) 310 { 311 blockSize--; 312 assert(blockSize > 0, "Key bits is too small."); 313 continue; 314 } 315 316 generateXteaKey(block); 317 return t; 318 } 319 } 320 } 321 322 ubyte[] ret; 323 324 size_t blockSize; 325 BigInt block = getNextBlock(blockSize); 326 if (blockSize == 0) 327 { 328 return ret; 329 } 330 331 block = BigIntHelper.powmod(block, key.exponent, key.modulus); 332 ubyte[] block_buf = BigIntHelper.toUBytes(block); 333 static if (T == "encrypt") 334 { 335 for (size_t i; i < keySize - block_buf.length; i++) 336 { 337 ret ~= cast(ubyte)0; 338 } 339 } 340 else 341 { 342 generateXteaKey(block_buf); 343 block_buf = block_buf[1 .. $]; 344 } 345 346 ret ~= block_buf; 347 348 if (blockSize >= data.length) 349 { 350 return ret; 351 } 352 353 data = data[blockSize .. $]; 354 355 static if (T == "encrypt") 356 { 357 ret ~= Xtea.encrypt(data, xteaKey, rounds, PaddingMode.Customized); 358 } 359 else 360 { 361 ret ~= Xtea.decrypt(data, xteaKey, rounds, PaddingMode.Customized); 362 } 363 364 return ret; 365 } 366 } 367 368 interface iPKCS 369 { 370 static string encodeKey(BigInt modulus, BigInt exponent); 371 static Nullable!RSAKeyInfo decodeKey(string key); 372 } 373 374 class SimpleFormat : iPKCS 375 { 376 static string encodeKey(BigInt modulus, BigInt exponent) 377 { 378 ubyte[] m_bytes = BigIntHelper.toUBytes(modulus); 379 ubyte[] e_bytes = BigIntHelper.toUBytes(exponent); 380 381 ubyte[] buffer = new ubyte[4]; 382 383 buffer.write!int(cast(int) m_bytes.length, 0); 384 buffer ~= m_bytes; 385 buffer ~= e_bytes; 386 387 return Base64.encode(buffer); 388 } 389 390 static Nullable!RSAKeyInfo decodeKey(string key) 391 { 392 ubyte[] buffer = Base64.decode(key); 393 int m_len = buffer.peek!int(0); 394 ubyte[] modulus_bytes = buffer[4 .. 4 + m_len]; 395 ubyte[] exponent_bytes = buffer[4 + m_len .. $]; 396 397 return Nullable!RSAKeyInfo(RSAKeyInfo(BigIntHelper.fromBytes(modulus_bytes), 398 modulus_bytes, BigIntHelper.fromBytes(exponent_bytes), exponent_bytes)); 399 } 400 } 401 402 class PKCS1 : iPKCS 403 { 404 static string encodeKey(BigInt modulus, BigInt exponent) 405 { 406 return string.init; 407 } 408 409 static Nullable!RSAKeyInfo decodeKey(string key) 410 { 411 return Nullable!RSAKeyInfo(); 412 } 413 } 414 415 class PKCS8 : iPKCS 416 { 417 static string encodeKey(BigInt modulus, BigInt exponent) 418 { 419 return string.init; 420 } 421 422 static Nullable!RSAKeyInfo decodeKey(string key) 423 { 424 return Nullable!RSAKeyInfo(); 425 } 426 } 427 428 unittest 429 { 430 import crypto.rsa; 431 432 RSAKeyPair keyPair = RSA.generateKeyPair(1024); 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 assert(cast(string) sb == data); 448 } 449 450 // for mixinXteaMode 451 unittest 452 { 453 import crypto.rsa; 454 455 RSAKeyInfo pri_key = RSA.decodeKey("AAAAgIcksjBHK7FYePatdsIWiHv7Z+HixIcD0dMyEtXLAGf9fDujn05Seyz1mTIE62UKMojVK+O4oxVT9Ljf8w5F81dTnanoKH0+I/lQN5CR95nRgUEwhZFSbmYM2YeACsfPyX1V5iP0KU8LN9D7/7q64lSZ6XbHGpa7DRv7yvDHaMyJBkGipi2FTk6EOxdIui+E3giDhKeU5ZM9sYNN7+vX9vh7Od+XTm7vGOO91dz4cNMKB9+mioJPunsKh0yG2hBO9Zg+IZNWir/jRfG/w38Av4pQOkRsz9U/ZsTA37XKqzglnwaKGPSw51RJuWuv9RXDRh12Po6oLnr5TkK0OBeJPgE="); 456 RSAKeyInfo pub_key = RSA.decodeKey("AAAAgIcksjBHK7FYePatdsIWiHv7Z+HixIcD0dMyEtXLAGf9fDujn05Seyz1mTIE62UKMojVK+O4oxVT9Ljf8w5F81dTnanoKH0+I/lQN5CR95nRgUEwhZFSbmYM2YeACsfPyX1V5iP0KU8LN9D7/7q64lSZ6XbHGpa7DRv7yvDHaMyJAQAB"); 457 458 string data = ` 459 And the workload proves (POW) reusable workload proof (RPOW) 2. hash function 460 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: 461 The input x can be a string of any length 462 The output, that is, the length of H (x) is fixed 463 464 The procedure for calculating H (x) is efficient (for string X of length n), the time complexity of H (x) should be O (n) 465 For bitcoin, the hash function used by such cryptographic systems, it needs to have the following properties: 466 `; 467 468 ubyte[] sb = cast(ubyte[]) data; 469 ubyte[] db = RSA.encrypt(pri_key, sb, true); 470 sb = RSA.decrypt(pub_key, db, true); 471 assert(cast(string) sb == data); 472 }