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     ubyte[32] secKey;
58     this(ubyte[32] key)
59     {
60         secKey = key;
61         ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
62 
63     }
64 
65     Signature sign(ubyte[] data)
66     {
67         ubyte[32] msgHash = keccak256(data);
68         secp256k1_ecdsa_recoverable_signature signature;
69         ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null);
70 
71         signature.data[].reverse;
72         return Signature(signature.data[0], signature.data[33 .. 65], signature.data[1 .. 33]);
73     }
74 
75     bool verify(ubyte[] data, Signature sig)
76     {
77         secp256k1_ecdsa_signature cSig;
78 
79         auto msgHash = keccak256(data);
80         cSig.data[32 .. 64] = sig.r;
81         cSig.data[0 .. 32] = sig.s;
82         cSig.data[].reverse;
83         secp256k1_pubkey pubKey;
84         assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr));
85         return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, &pubKey);
86 
87     }
88 
89     ubyte[20] address() @property const
90     {
91         secp256k1_pubkey pubKey;
92         assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr));
93         return pubKey.toAddress;
94     }
95 
96     ~this()
97     {
98         secp256k1_context_destroy(ctx);
99     }
100 }
101 
102 ubyte[20] toAddress(secp256k1_pubkey pubKey)
103 {
104     import std.algorithm : reverse;
105 
106     pubKey.data[0 .. 32].reverse;
107     pubKey.data[32 .. $].reverse;
108     return keccak256(pubKey.data[])[12 .. $];
109 
110 }
111 
112 ubyte[20] ecRecover(Signature sig, ubyte[] msg)
113 {
114     auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
115     secp256k1_pubkey pubKey;
116     secp256k1_ecdsa_recoverable_signature cSig;
117     cSig.data[0] = cast(ubyte) sig.recid;
118     cSig.data[33 .. 65] = sig.r;
119     cSig.data[1 .. 33] = sig.s;
120     cSig.data[].reverse;
121     auto msgHash = keccak256(msg);
122     ctx.secp256k1_ecdsa_recover(&pubKey, &cSig, msgHash.ptr);
123     return toAddress(pubKey);
124 
125 }