1 module tagion.betterC.wallet.Net; 2 3 // import tagion.crypto.aes.AESCrypto; 4 import std.format; 5 import std.string : representation; 6 import tagion.betterC.utils.BinBuffer; 7 private import tagion.crypto.secp256k1.c.secp256k1; 8 private import tagion.crypto.secp256k1.c.secp256k1_ecdh; 9 import tagion.betterC.utils.Memory; 10 import hash = tagion.betterC.wallet.hash; 11 12 // import tagion.betterC.hibon.Document; 13 14 enum HASH_SIZE = 32; 15 enum UNCOMPRESSED_PUBKEY_SIZE = 65; 16 enum COMPRESSED_PUBKEY_SIZE = 33; 17 enum SECKEY_SIZE = 32; 18 19 @trusted 20 void scramble(T)(scope ref T[] data, scope const(ubyte[]) xor = null) if (T.sizeof is 1) { 21 // import std.random; 22 ubyte[] seed; 23 seed.create(data.length); 24 25 randomize(cast(immutable) seed); 26 foreach (i; data) { 27 data[i] ^= seed[i]; 28 } 29 } 30 31 @trusted uint hashSize() pure nothrow { 32 return HASH_SIZE; 33 } 34 35 @trusted BinBuffer rawCalcHash(scope const(ubyte[]) data) { 36 BinBuffer res; 37 res.write(hash.secp256k1_count_hash(data)); 38 39 return res; 40 } 41 42 @trusted immutable(ubyte[]) rawCalcHash(const BinBuffer buffer) { 43 BinBuffer res; 44 res.write(hash.secp256k1_count_hash(buffer.serialize)); 45 46 return res.serialize; 47 } 48 49 @trusted immutable(BinBuffer) calcHash(scope const(ubyte[]) data) { 50 return cast(immutable) rawCalcHash(data); 51 } 52 53 @trusted immutable(BinBuffer) calcHash(scope const(ubyte[]) h1, scope const(ubyte[]) h2) { 54 BinBuffer res; 55 if (h1.length !is 0 && h2.length !is 0) { 56 ubyte[] concatenat; 57 concatenat.create(h1.length + h2.length); 58 concatenat[0 .. h1.length] = h1; 59 concatenat[h1.length .. $] = h2; 60 return cast(immutable) rawCalcHash(concatenat); 61 } 62 else if (h1.length is 0) { 63 res.write(h2); 64 } 65 else if (h2.length is 0) { 66 res.write(h1); 67 } 68 69 return cast(immutable) res; 70 } 71 72 struct AES { 73 enum KEY_LENGTH = 256; 74 enum KEY_SIZE = KEY_LENGTH / 8; 75 enum BLOCK_SIZE = 16; 76 77 static size_t enclength(const size_t inputlength) { 78 return ((inputlength / BLOCK_SIZE) + ((inputlength % BLOCK_SIZE == 0) ? 0 : 1)) * BLOCK_SIZE; 79 } 80 81 import tagion.crypto.aes.tiny_aes.tiny_aes; 82 83 alias T_AES = Tiny_AES!(KEY_LENGTH, Mode.CBC); 84 85 static void crypt_parse(bool ENCRYPT = true)(const(ubyte[]) key, ubyte[BLOCK_SIZE] iv, ref ubyte[] data) { 86 scope aes = T_AES(key[0 .. KEY_SIZE], iv); 87 static if (ENCRYPT) { 88 aes.encrypt(data); 89 } 90 else { 91 aes.decrypt(data); 92 } 93 } 94 95 static void crypt(bool ENCRYPT = true)(scope const(ubyte[]) key, scope const(ubyte[]) iv, scope const( 96 ubyte[]) indata, ref ubyte[] outdata) { 97 if (outdata is null) { 98 outdata.create(indata.length); 99 } 100 outdata[0 .. $] = indata[0 .. $]; 101 size_t old_length; 102 if (outdata.length % BLOCK_SIZE !is 0) { 103 old_length = outdata.length; 104 // outdata.length = enclength(outdata.length); 105 outdata.create(enclength(outdata.length)); 106 } 107 ubyte[BLOCK_SIZE] temp_iv = iv[0 .. BLOCK_SIZE]; 108 crypt_parse!ENCRYPT(key, temp_iv, outdata); 109 } 110 111 alias encrypt = crypt!true; 112 alias decrypt = crypt!false; 113 } 114 115 struct SecureNet { 116 import tagion.crypto.Types : Pubkey, Signature; 117 118 private Pubkey _pubkey; 119 // // private SignDelegate _crypt; 120 // immutable(ubyte[]) delegate(const(ubyte[])) sign_dg; 121 122 enum DER_SIGNATURE_SIZE = 72; 123 enum SIGNATURE_SIZE = 64; 124 private secp256k1_context* _ctx; 125 126 enum Format { 127 DER = 1, 128 COMPACT = DER << 1, 129 RAW = COMPACT << 1, 130 AUTO = RAW | DER | COMPACT 131 } 132 133 private Format _format_verify; 134 private Format _format_sign; 135 136 @trusted 137 immutable(ubyte[]) sign(const(ubyte[]) data, const(ubyte[]) sec) const 138 in { 139 assert(data.length == 32); 140 assert(sec.length <= 32); 141 } 142 do { 143 ubyte[] result; 144 const msgdata = data.ptr; 145 const secKey = sec.ptr; 146 secp256k1_ecdsa_signature sig_array; 147 secp256k1_ecdsa_signature* sig = &sig_array; 148 149 int ret = secp256k1_ecdsa_sign(_ctx, sig, msgdata, secKey, null, null); 150 if (_format_sign is Format.DER) { 151 ubyte[DER_SIGNATURE_SIZE] outputSer_array; 152 ubyte* outputSer = outputSer_array.ptr; 153 size_t outputLen = outputSer_array.length; 154 ret = secp256k1_ecdsa_signature_serialize_der(_ctx, outputSer, &outputLen, sig); 155 if (ret) { 156 result.create(outputLen); 157 result[0 .. $] = outputSer_array[0 .. outputLen]; 158 // immutable(ubyte[]) result = outputSer_array[0 .. outputLen].idup; 159 return cast(immutable) result; 160 } 161 } 162 if (_format_sign is Format.COMPACT) { 163 ubyte[SIGNATURE_SIZE] outputSer_array; 164 ubyte* outputSer = outputSer_array.ptr; 165 // size_t outputLen = outputSer_array.length; 166 ret = secp256k1_ecdsa_signature_serialize_compact(_ctx, outputSer, sig); 167 if (ret) { 168 // immutable(ubyte[]) result = outputSer_array.idup; 169 result.create(outputSer_array.length); 170 result[0 .. $] = outputSer_array[0 .. $]; 171 return cast(immutable) result; 172 } 173 } 174 // writefln("Format=%s", _format_sign); 175 result.create(SIGNATURE_SIZE); 176 result[0 .. $] = sig.data[0 .. SIGNATURE_SIZE]; 177 // immutable(ubyte[]) result = sig.data[0 .. SIGNATURE_SIZE].idup; 178 return cast(immutable) result; 179 } 180 181 // Signature sign(const(ubyte[]) message) const 182 // in { 183 // assert(message.length == 32); 184 // } 185 // do { 186 // import std.traits; 187 188 // assert(_secret !is null, format("Signature function has not been intialized. Use the %s function", fullyQualifiedName!generateKeyPair)); 189 190 // return Signature(sign_dg(message)); 191 // } 192 193 @trusted 194 void privKeyTweakMul(const(ubyte[]) privkey, const(ubyte[]) tweak, ref ubyte[] tweak_privkey) const 195 in { 196 assert(privkey.length == 32); 197 } 198 do { 199 pragma(msg, "fixme(cbr): privkey must be scrambled"); 200 tweak_privkey[0 .. privkey.length] = privkey[0 .. $]; 201 ubyte* _privkey = tweak_privkey.ptr; 202 const(ubyte)* _tweak = tweak.ptr; 203 204 int ret = secp256k1_ec_privkey_tweak_mul(_ctx, _privkey, _tweak); 205 } 206 207 @trusted 208 void privKeyTweakAdd(const(ubyte[]) privkey, const(ubyte[]) tweak, ref ubyte[] tweak_privkey) const 209 in { 210 assert(privkey.length == 32); 211 } 212 do { 213 // auto ctx=getContext(); 214 pragma(msg, "fixme(cbr): privkey must be scrambled"); 215 tweak_privkey[0 .. privkey.length] = privkey[0 .. $]; 216 ubyte* _privkey = tweak_privkey.ptr; 217 const(ubyte)* _tweak = tweak.ptr; 218 219 int ret = secp256k1_ec_privkey_tweak_add(_ctx, _privkey, _tweak); 220 } 221 222 @trusted 223 immutable(ubyte[]) pubKeyTweakMul(const(ubyte[]) pubkey, const(ubyte[]) tweak, immutable bool compress = true) const { 224 // auto ctx=getContext(); 225 ubyte[] pubkey_array; 226 pubkey_array.create(pubkey.length); 227 pubkey_array[0 .. $] = pubkey[0 .. $]; 228 ubyte* _pubkey = pubkey_array.ptr; 229 const(ubyte)* _tweak = tweak.ptr; 230 size_t publen = pubkey.length; 231 232 secp256k1_pubkey pubkey_result; 233 int ret = secp256k1_ec_pubkey_parse(_ctx, &pubkey_result, _pubkey, publen); 234 235 ret = secp256k1_ec_pubkey_tweak_mul(_ctx, &pubkey_result, _tweak); 236 237 ubyte[] outputSer_array; 238 SECP256K1 flag; 239 if (compress) { 240 outputSer_array.create(COMPRESSED_PUBKEY_SIZE); 241 // outputSer_array = new ubyte[COMPRESSED_PUBKEY_SIZE]; 242 flag = SECP256K1.EC_COMPRESSED; 243 } 244 else { 245 outputSer_array.create(UNCOMPRESSED_PUBKEY_SIZE); 246 // outputSer_array = new ubyte[UNCOMPRESSED_PUBKEY_SIZE]; 247 flag = SECP256K1.EC_UNCOMPRESSED; 248 } 249 250 ubyte* outputSer = outputSer_array.ptr; 251 size_t outputLen = outputSer_array.length; 252 253 int ret2 = secp256k1_ec_pubkey_serialize(_ctx, outputSer, &outputLen, &pubkey_result, flag); 254 255 return cast(immutable)(outputSer_array); 256 } 257 258 @trusted 259 immutable(ubyte[]) computePubkey(scope const(ubyte[]) seckey, immutable bool compress = true) const 260 in { 261 assert(seckey.length == SECKEY_SIZE); 262 } 263 out (result) { 264 if (compress) { 265 assert(result.length == COMPRESSED_PUBKEY_SIZE); 266 } 267 else { 268 assert(result.length == UNCOMPRESSED_PUBKEY_SIZE); 269 } 270 } 271 do { 272 // auto ctx=getContext(); 273 const(ubyte)* sec = seckey.ptr; 274 275 secp256k1_pubkey pubkey; 276 277 int ret = secp256k1_ec_pubkey_create(_ctx, &pubkey, sec); 278 // ubyte[pubkey_size] outputSer_array; 279 ubyte[] outputSer_array; 280 SECP256K1 flag; 281 if (compress) { 282 outputSer_array.create(COMPRESSED_PUBKEY_SIZE); 283 // outputSer_array = new ubyte[COMPRESSED_PUBKEY_SIZE]; 284 flag = SECP256K1.EC_COMPRESSED; 285 } 286 else { 287 outputSer_array.create(UNCOMPRESSED_PUBKEY_SIZE); 288 // outputSer_array = new ubyte[UNCOMPRESSED_PUBKEY_SIZE]; 289 flag = SECP256K1.EC_UNCOMPRESSED; 290 } 291 ubyte* outputSer = outputSer_array.ptr; 292 size_t outputLen = outputSer_array.length; 293 294 int ret2 = secp256k1_ec_pubkey_serialize(_ctx, outputSer, &outputLen, &pubkey, flag); 295 296 // immutable(ubyte[]) result = outputSer_array[0 .. outputLen].idup; 297 ubyte[] result; 298 result.create(outputLen); 299 result[0 .. $] = outputSer_array[0 .. outputLen]; 300 return cast(immutable) result; 301 } 302 303 @trusted BinBuffer createECDHSecret(scope const(ubyte[]) seckey, const( 304 ubyte[]) pubkey) const 305 in { 306 assert(seckey.length <= SECKEY_SIZE); 307 assert(pubkey.length <= UNCOMPRESSED_PUBKEY_SIZE); 308 } 309 do { 310 // auto ctx=getContext(); 311 const secdata = seckey.ptr; 312 const pubdata = pubkey.ptr; 313 size_t publen = pubkey.length; 314 315 secp256k1_pubkey pubkey_result; 316 ubyte[] result; 317 result.create(SECKEY_SIZE); 318 ubyte* _result = result.ptr; 319 320 int ret = secp256k1_ec_pubkey_parse(_ctx, &pubkey_result, pubdata, publen); 321 322 //if (ret) { 323 ret = secp256k1_ecdh(_ctx, _result, &pubkey_result, secdata, null, null); 324 //} 325 BinBuffer buf_res; 326 buf_res.write(result); 327 328 return buf_res; 329 } 330 331 @trusted bool secKeyVerify(scope const(ubyte[]) seckey) const 332 in { 333 assert(seckey.length == 32); 334 } 335 do { 336 const(ubyte)* sec = seckey.ptr; 337 return secp256k1_ec_seckey_verify(_ctx, sec) == 1; 338 } 339 340 @trusted void createKeyPair(ref ubyte[] privkey) 341 in { 342 assert(secKeyVerify(privkey)); 343 } 344 do { 345 import std.string : representation; 346 347 _pubkey = computePubkey(privkey); 348 // Generate scramble key for the private key 349 import std.random; 350 351 ubyte[] seed; 352 seed.create(32); 353 354 scramble(seed); 355 // CBR: Note AES need to be change to beable to handle const keys 356 auto aes_key = rawCalcHash(seed); 357 scramble(seed); 358 auto ase_pre_iv = rawCalcHash(seed); 359 auto aes_iv = ase_pre_iv[4 .. 4 + AES.BLOCK_SIZE]; 360 361 // Encrypt private key 362 // auto encrypted_privkey = new ubyte[privkey.length]; 363 ubyte[] encrypted_privkey; 364 encrypted_privkey.create(privkey.length); 365 AES.encrypt(aes_key.serialize, aes_iv.serialize, privkey, encrypted_privkey); 366 367 AES.encrypt(rawCalcHash(seed).serialize, aes_iv.serialize, encrypted_privkey, privkey); 368 scramble(seed); 369 370 AES.encrypt(aes_key.serialize, aes_iv.serialize, encrypted_privkey, privkey); 371 372 AES.encrypt(aes_key.serialize, aes_iv.serialize, privkey, seed); 373 374 AES.encrypt(aes_key.serialize, aes_iv.serialize, encrypted_privkey, privkey); 375 376 void do_secret_stuff(scope void delegate(const(ubyte[]) privkey) @safe dg) { 377 // CBR: 378 // Yes I know it is security by obscurity 379 // But just don't want to have the private in clear text in memory 380 // for long period of time 381 // auto privkey = new ubyte[encrypted_privkey.length]; 382 383 ubyte[] privkey; 384 privkey.create(encrypted_privkey); 385 scope (exit) { 386 ubyte[] seed; 387 seed.create(32); 388 scramble(seed, aes_key.serialize); 389 // problems with immutable stuff - know how to fix, but need to rewrite prev then 390 // scramble(aes_key.serialize, seed); 391 // scramble(aes_iv.serialize); 392 AES.encrypt(aes_key.serialize, aes_iv.serialize, privkey, encrypted_privkey); 393 AES.encrypt(rawCalcHash(seed).serialize, aes_iv.serialize, encrypted_privkey, privkey); 394 } 395 AES.decrypt(aes_key.serialize, aes_iv.serialize, encrypted_privkey, privkey); 396 dg(privkey); 397 } 398 // struct SignWrapper { 399 @trusted 400 immutable(ubyte[]) raw_sign(const(ubyte[]) data, const(ubyte[]) sec) const 401 in { 402 assert(data.length == 32); 403 assert(sec.length <= 32); 404 } 405 do { 406 ubyte[] result; 407 const msgdata = data.ptr; 408 const secKey = sec.ptr; 409 secp256k1_ecdsa_signature sig_array; 410 secp256k1_ecdsa_signature* sig = &sig_array; 411 412 // int ret = secp256k1_ecdsa_sign(_ctx, sig, msgdata, secKey, null, null); 413 // check(ret == 1, ConsensusFailCode.SECURITY_SIGN_FAULT); 414 // if (_format_sign is Format.DER) { 415 // ubyte[DER_SIGNATURE_SIZE] outputSer_array; 416 // ubyte* outputSer = outputSer_array.ptr; 417 // size_t outputLen = outputSer_array.length; 418 // ret = secp256k1_ecdsa_signature_serialize_der(_ctx, outputSer, &outputLen, sig); 419 // if (ret) { 420 // immutable(ubyte[]) result = outputSer_array[0 .. outputLen].idup; 421 // return result; 422 // } 423 // } 424 // if (_format_sign is Format.COMPACT) { 425 // ubyte[SIGNATURE_SIZE] outputSer_array; 426 // ubyte* outputSer = outputSer_array.ptr; 427 // // size_t outputLen = outputSer_array.length; 428 // ret = secp256k1_ecdsa_signature_serialize_compact(_ctx, outputSer, sig); 429 // if (ret) { 430 // immutable(ubyte[]) result = outputSer_array.idup; 431 // return result; 432 // } 433 // } 434 // // writefln("Format=%s", _format_sign); 435 // immutable(ubyte[]) result = sig.data[0 .. SIGNATURE_SIZE].idup; 436 return cast(immutable) result; 437 } 438 439 @trusted immutable(ubyte[]) sign(const(ubyte[]) message) const { 440 immutable(ubyte)[] result; 441 do_secret_stuff((const(ubyte[]) privkey) { result = raw_sign(message, privkey); }); 442 return result; 443 } 444 // } 445 // SignWrapper sign_wrapper; 446 // sign_dg = &sign_wrapper.sign; 447 448 void tweakMul(const(ubyte[]) tweak_code, ref ubyte[] tweak_privkey) { 449 do_secret_stuff((const(ubyte[]) privkey) @safe { privKeyTweakMul(privkey, tweak_code, tweak_privkey); }); 450 } 451 452 void tweakAdd(const(ubyte[]) tweak_code, ref ubyte[] tweak_privkey) { 453 do_secret_stuff((const(ubyte[]) privkey) @safe { privKeyTweakAdd(privkey, tweak_code, tweak_privkey); }); 454 } 455 456 immutable(ubyte[]) ECDHSecret(const(ubyte[]) pubkey) const { 457 BinBuffer result; 458 do_secret_stuff((const(ubyte[]) privkey) @trusted { result = createECDHSecret(privkey, pubkey); }); 459 return result.serialize; 460 } 461 } 462 463 @trusted immutable(ubyte[]) HMAC(scope const(ubyte[]) data) const { 464 465 scope hmac = hash.secp256k1_count_hmac_hash(data); 466 auto res_size = hmac.length; 467 ubyte[] result; 468 result.create(res_size); 469 result[0 .. $] = hmac[0 .. $]; 470 return cast(immutable) result; 471 } 472 473 Pubkey pubkey() pure const nothrow { 474 return _pubkey; 475 } 476 477 Pubkey derivePubkey(string tweak_word) const { 478 const tweak_code = HMAC(tweak_word.representation); 479 return derivePubkey(tweak_code); 480 } 481 482 Pubkey derivePubkey(const(ubyte[]) tweak_code) const { 483 Pubkey result; 484 const pkey = cast(const(ubyte[])) _pubkey; 485 result = pubKeyTweakMul(pkey, tweak_code); 486 return result; 487 } 488 489 Pubkey derivePubkey(BinBuffer tweak_buf) const { 490 Pubkey result; 491 // // ubyte[] tweak_arr; 492 // // tweak_arr.create(tweak_buf.length); 493 // // tweak_arr[0..$] = tweak_buf[0..$]; 494 // // return derivePubkey(tweak_arr); 495 return result; 496 } 497 498 @trusted 499 bool verify(const(ubyte[]) data, const(ubyte[]) signature, const(ubyte[]) pub) const 500 in { 501 assert(data.length == 32); 502 assert(signature.length <= 520); 503 assert(pub.length <= 520); 504 } 505 do { 506 int ret; 507 const sigdata = signature.ptr; 508 auto siglen = signature.length; 509 const pubdata = pub.ptr; 510 const msgdata = data.ptr; 511 512 secp256k1_ecdsa_signature sig; 513 secp256k1_pubkey pubkey; 514 if (_format_verify & Format.DER) { 515 ret = secp256k1_ecdsa_signature_parse_der(_ctx, &sig, sigdata, siglen); 516 } 517 if (ret) { 518 goto PARSED; 519 } 520 if (_format_verify & Format.COMPACT) { 521 ret = secp256k1_ecdsa_signature_parse_compact(_ctx, &sig, sigdata); 522 } 523 if (ret) { 524 goto PARSED; 525 } 526 if ((_format_verify & Format.RAW) || (_format_verify == 0)) { 527 import core.stdc.string : memcpy; 528 529 memcpy(&(sig.data), sigdata, siglen); 530 } 531 PARSED: 532 auto publen = pub.length; 533 ret = secp256k1_ec_pubkey_parse(_ctx, &pubkey, pubdata, publen); 534 535 ret = secp256k1_ecdsa_verify(_ctx, &sig, msgdata, &pubkey); 536 return ret == 1; 537 } 538 539 }