1 module secp256k1;
2 
3 import std.stdio;
4 import std.exception : enforce;
5 import std.conv : to, text;
6 import std : toHexString;
7 import std.algorithm : reverse;
8 
9 import include.secp256k1_recovery;
10 import include.secp256k1;
11 
12 import keccak : keccak256;
13 
14 alias bytes = ubyte[];
15 unittest
16 {
17     writeln("Elliptic Curve example");
18     ubyte[32] pk;
19     pk[0 .. $] = "beb75b08049e9316d1375999c7d968f3c23fdf606b296fcdfc9a41cdd7e7347c".hexToBytes;
20     auto ec = new secp256k1(pk);
21     auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa");
22     ec.writeln;
23     ec.sign(msg).writeln;
24     ec.address.toHexString.writeln(" --  address");
25     ec.sign(msg).ecRecover(msg).toHexString.writeln(" -- recovered address");
26     foreach (i; 0 .. 500)
27     {
28         auto sign = ec.sign(msg);
29         auto recAddr = sign.ecRecover(msg);
30         assert(recAddr == ec.address, text(sign, recAddr));
31     }
32     assert(ec.verify(msg, ec.sign(msg)));
33 }
34 
35 private bytes hexToBytes(string s)
36 {
37     import std : chunks, map, array, startsWith, replace;
38     import std.range : padLeft;
39 
40     s = s.replace("_", "").replace(" ", "");
41     if (s.startsWith(`0x`))
42         s = s[2 .. $];
43 
44     return s.padLeft('0', s.length + s.length % 2).chunks(2).map!q{a.parse!ubyte(16)}.array;
45 }
46 
47 struct Signature
48 {
49     int recid;
50     ubyte[32] r;
51     ubyte[32] s;
52 }
53 
54 class secp256k1
55 {
56     private secp256k1_context* ctx;
57     bool canSign;
58     bool canVerify;
59     ubyte[32] secKey;
60     ubyte[64] pubKey;
61 
62     this()
63     {
64         ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
65         do
66         {
67             import random = std.random;
68 
69             random.uniform!"[]"(ulong.min, ulong.max);
70             static immutable secKeySize = 32 / ulong.sizeof;
71             foreach (ref e; cast(ulong[secKeySize]) secKey)
72             {
73                 e = random.uniform(ulong.min, ulong.max);
74             }
75         }
76         while (!secp256k1_ec_seckey_verify(ctx, secKey.ptr));
77         canSign = canVerify = true;
78         assert(secp256k1_ec_pubkey_create(ctx, cast(secp256k1_pubkey*)&pubKey, secKey.ptr));
79     }
80 
81     this(ubyte[32] key)
82     {
83         canSign = canVerify = true;
84         secKey = key;
85         ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
86         assert(secp256k1_ec_pubkey_create(ctx, cast(secp256k1_pubkey*)&pubKey, secKey.ptr));
87     }
88 
89     this(ubyte[64] pubKey)
90     {
91         canVerify = true;
92         ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
93         this.pubKey = pubKey;
94     }
95 
96     Signature sign(ubyte[] data)
97     {
98         ubyte[32] msgHash = keccak256(data);
99         return signHash(msgHash);
100     }
101 
102     Signature signHash(ubyte[32] msgHash)
103     {
104         canSign.enforce("this key cannot sign: secKey not inited");
105         secp256k1_ecdsa_recoverable_signature signature;
106         ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null);
107 
108         signature.data[].reverse;
109         return Signature(signature.data[0], signature.data[33 .. 65], signature.data[1 .. 33]);
110     }
111 
112     bool verify(ubyte[] data, Signature sig)
113     {
114         secp256k1_ecdsa_signature cSig;
115 
116         auto msgHash = keccak256(data);
117         cSig.data[32 .. 64] = sig.r;
118         cSig.data[0 .. 32] = sig.s;
119         cSig.data[].reverse;
120         return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr,
121                 cast(secp256k1_pubkey*)&pubKey);
122 
123     }
124 
125     ubyte[20] address() @property const
126     {
127         return pubKey.secp256k1_pubkey.toAddress;
128     }
129 
130     ~this()
131     {
132         secp256k1_context_destroy(ctx);
133     }
134 }
135 
136 ubyte[20] toAddress(secp256k1_pubkey pubKey)
137 {
138     import std.algorithm : reverse;
139 
140     pubKey.data[0 .. 32].reverse;
141     pubKey.data[32 .. $].reverse;
142     return keccak256(pubKey.data[])[12 .. $];
143 
144 }
145 
146 ubyte[20] ecRecover(Signature sig, ubyte[] msg)
147 {
148     auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
149     secp256k1_pubkey pubKey;
150     secp256k1_ecdsa_recoverable_signature cSig;
151     cSig.data[0] = cast(ubyte) sig.recid;
152     cSig.data[33 .. 65] = sig.r;
153     cSig.data[1 .. 33] = sig.s;
154     cSig.data[].reverse;
155     auto msgHash = keccak256(msg);
156     ctx.secp256k1_ecdsa_recover(cast(secp256k1_pubkey*)&pubKey, &cSig, msgHash.ptr);
157     return toAddress(pubKey);
158 
159 }
160 
161 unittest
162 {
163     import std.stdio;
164 
165     writeln("Leading zero seach test");
166     foreach (i; 0 .. 10_000)
167     {
168         auto a = new secp256k1;
169         auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa");
170         auto sig = a.sign(msg);
171         assert(a.verify(msg, sig));
172         auto addr = a.address.toHexString;
173         if (addr.leadingZeros > 2)
174         {
175             addr.writeln;
176         }
177     }
178 
179 }
180 
181 auto leadingZeros(T)(T s)
182 {
183 
184     foreach (i, c; s)
185     {
186         if (c != '0')
187             return i;
188     }
189     return s.length;
190 }