1 module secp256k1;
2 import include.secp256k1_recovery;
3 import include.secp256k1;
4 import std.stdio;
5 import std.exception : enforce;
6 import std.conv : to, text;
7 import std : toHexString;
8 
9 import core.stdc.stdlib : free;
10 import keccak : keccak_256, keccak256;
11 
12 alias bytes = ubyte[];
13 unittest
14 {
15     writeln("Elliptic Curve example");
16     ubyte[32] pk;
17     pk[0 .. $] = "beb75b08049e9316d1375999c7d968f3c23fdf606b296fcdfc9a41cdd7e7347c".hexToBytes;
18     auto ec = new secp256k1(pk);
19     auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa");
20     ec.writeln;
21     ec.sign(msg).writeln;
22     ec.address.toHexString.writeln(" --  address");
23     ec.sign(msg).ecRecover(msg).toHexString.writeln("-- recovered address");
24     foreach (i; 0 .. 500)
25     {
26         auto sign = ec.sign(msg);
27         auto recAddr = sign.ecRecover(msg);
28         assert(recAddr == ec.address, text(sign, recAddr));
29     }
30     assert(ec.verify(msg, ec.sign(msg)));
31 }
32 
33 bytes hexToBytes(string s)
34 {
35     import std : chunks, map, array, startsWith, replace;
36     import std.range : padLeft;
37 
38     s = s.replace("_", "").replace(" ", "");
39     if (s.startsWith(`0x`))
40         s = s[2 .. $];
41 
42     return s.padLeft('0', s.length + s.length % 2).chunks(2).map!q{a.parse!ubyte(16)}.array;
43 }
44 
45 struct Signature
46 {
47     int recid;
48     ubyte[32] r;
49     ubyte[32] s;
50 }
51 
52 class secp256k1
53 {
54     private secp256k1_context* ctx;
55     ubyte[32] secKey;
56     this(ubyte[32] key)
57     {
58         secKey = key;
59         ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
60 
61     }
62 
63     Signature sign(ubyte[] data)
64     {
65         ubyte[32] msgHash = keccak256(data);
66         secp256k1_ecdsa_recoverable_signature signature;
67         ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null);
68         return Signature(signature.data[64], signature.data[0 .. 32], signature.data[32 .. 64]);
69     }
70 
71     bool verify(ubyte[] data, Signature sig)
72     {
73         secp256k1_ecdsa_signature cSig;
74 
75         auto msgHash = keccak256(data);
76         cSig.data[0 .. 32] = sig.r;
77         cSig.data[32 .. 64] = sig.s;
78         secp256k1_pubkey pubKey;
79         assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr));
80         return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, &pubKey);
81 
82     }
83 
84     ubyte[20] address() @property const
85     {
86         import std.algorithm : reverse;
87 
88         secp256k1_pubkey pubKey;
89         assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr));
90         return pubKey.toAddress;
91     }
92 
93     ~this()
94     {
95         secp256k1_context_destroy(ctx);
96     }
97 }
98 
99 ubyte[20] toAddress(secp256k1_pubkey pubKey)
100 {
101     import std.algorithm : reverse;
102 
103     pubKey.data[0 .. 32].reverse;
104     pubKey.data[32 .. $].reverse;
105     return keccak256(pubKey.data[])[12 .. $];
106 
107 }
108 
109 ubyte[20] ecRecover(Signature sig, ubyte[] msg)
110 {
111     auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
112     secp256k1_pubkey pubKey;
113     secp256k1_ecdsa_recoverable_signature cSig;
114     cSig.data[64] = cast(ubyte) sig.recid;
115     cSig.data[0 .. 32] = sig.r;
116     cSig.data[32 .. 64] = sig.s;
117     auto msgHash = keccak256(msg);
118     ctx.secp256k1_ecdsa_recover(&pubKey, &cSig, msgHash.ptr);
119     return toAddress(pubKey);
120 
121 }