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 }