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