1 module secp256k1;
2 
3 import std.stdio;
4 import std.exception : enforce;
5 import std.conv : to;
6 
7 import core.stdc.stdlib : free;
8 import keccak : keccak_256;
9 import deimos.openssl.ec;
10 import deimos.openssl.evp;
11 import deimos.openssl.pem;
12 import deimos.openssl.ecdsa;
13 import deimos.openssl.bn;
14 import deimos.openssl.obj_mac : NID_secp256k1;
15 
16 alias bytes = ubyte[];
17 unittest
18 {
19     writeln("Elliptic Curve example");
20     auto pk = new ubyte[32];
21     pk[0 .. $] = [
22         50, 93, 176, 197, 179, 11, 107, 161, 16, 47, 114, 191, 181, 146, 4, 60,
23         66, 29, 253, 65, 237, 252, 116, 78, 218, 69, 204, 32, 73, 168, 217, 123
24     ];
25     auto ec = new secp256k1(pk);
26     ec.writeln;
27     ec.address.writeln(" --  address");
28     ec.sign("abababa").writeln;
29     ec.sign("abababa").writeln;
30     ec.sign("abababa").writeln;
31     ec.sign("abababa").writeln;
32 }
33 
34 struct Signature
35 {
36     int recid;
37     ubyte[32] r;
38     ubyte[32] s;
39 }
40 
41 class secp256k1
42 {
43     EC_KEY* ec_key;
44 
45     this(ubyte[] pk = null)
46     {
47         ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);
48         if (pk == null)
49         {
50             EC_KEY_generate_key(ec_key);
51         }
52         else
53         {
54             auto bn_pk = BN_bin2bn(pk.ptr, cast(int) pk.length, null);
55             EC_KEY_set_private_key(ec_key, bn_pk);
56             auto group = EC_KEY_get0_group(ec_key);
57             auto pub_key = EC_POINT_new(group);
58             auto ctx = BN_CTX_new();
59             scope (exit)
60             {
61                 BN_free(bn_pk);
62                 BN_CTX_free(ctx);
63                 EC_POINT_free(pub_key);
64             }
65             EC_POINT_mul(group, pub_key, bn_pk, null, null, ctx);
66             EC_KEY_set_public_key(ec_key, pub_key);
67         }
68     }
69 
70     auto sign(Result = Signature, Input)(Input data)
71     {
72         ubyte[] _data = cast(ubyte[]) data.dup;
73         auto group = ec_key.EC_KEY_get0_group;
74         BIGNUM* kinv, rp, k = BN_new, Rx = BN_new, Ry = BN_new, order = BN_new;
75         ECDSA_SIG* sig;
76         EC_POINT* R = EC_POINT_new(group);
77         scope (exit)
78         {
79             foreach (bn; [order, k, kinv, rp, Rx, Ry])
80                 bn.BN_free;
81             EC_POINT_free(R);
82             ECDSA_SIG_free(sig);
83         }
84         EC_GROUP_get_order(group, order, null);
85         ECDSA_sign_setup(ec_key, null, &kinv, &rp);
86         BN_mod_inverse(k, kinv, order, null);
87         assert(EC_POINT_mul(group, R, k, null, null, null));
88         EC_POINT_get_affine_coordinates_GFp(group, R, Rx, Ry, null);
89         sig = ECDSA_do_sign_ex(_data.ptr, cast(int) _data.length, kinv, rp, ec_key);
90 
91         assert(ECDSA_do_verify(_data.ptr, cast(int) _data.length, sig, ec_key), "Check signature");
92         assert(0 == BN_cmp(sig.r, Rx), "Rx == r");
93 
94         Signature result;
95         result.recid = BN_is_odd(Ry);
96         BN_bn2bin(sig.r, result.r.ptr);
97         BN_bn2bin(sig.s, result.s.ptr);
98         return result;
99     }
100 
101     override string toString()
102     {
103         auto pkBN = EC_KEY_get0_private_key(ec_key);
104         auto cptr = BN_bn2hex(pkBN);
105         ubyte[32] pkBytes;
106         BN_bn2bin(pkBN, pkBytes.ptr);
107         string result = cptr.to!string ~ pkBytes.to!string ~ " Can sign: " ~ EC_KEY_check_key(ec_key)
108             .to!string;
109         OPENSSL_free(cptr);
110         return result;
111     }
112 
113     ~this()
114     {
115         EC_KEY_free(ec_key);
116     }
117 
118     auto publicKey()
119     {
120         auto pubKey = EC_POINT_point2bn(ec_key.EC_KEY_get0_group, ec_key.EC_KEY_get0_public_key,
121                 point_conversion_form_t.POINT_CONVERSION_UNCOMPRESSED, null, null);
122         ubyte[65] data;
123         BN_bn2bin(pubKey, data.ptr);
124 
125         scope (exit)
126         {
127             BN_free(pubKey);
128         }
129         return data;
130     }
131 
132     ubyte[20] address()
133     {
134         ubyte[32] hash;
135         auto pubKey = publicKey[1 .. $] ~ [];
136         keccak_256(hash.ptr, hash.length, pubKey.ptr, pubKey.length);
137         return hash[12 .. $];
138     }
139 
140     private auto ECDSA_SIG_to(Result)(ECDSA_SIG* sig)
141     {
142         static if (is(Result == string))
143         {
144             auto rptr = BN_bn2hex(sig.r);
145             auto sptr = BN_bn2hex(sig.s);
146             auto result = rptr.to!string ~ BN_bn2hex(sig.s).to!string;
147             scope (exit)
148             {
149                 free(rptr);
150                 free(sptr);
151             }
152         }
153         else static if (is(Result == bytes))
154         {
155             ubyte[32] r;
156             ubyte[32] s;
157             BN_bn2bin(sig.r, r.ptr);
158             BN_bn2bin(sig.s, s.ptr);
159             auto result = r[] ~ s[];
160         }
161         else static if (is(Result == Signature))
162         {
163             Signature result;
164 
165             auto group = ec_key.EC_KEY_get0_group;
166             auto R = EC_POINT_new(group);
167             EC_POINT_set_compressed_coordinates_GFp(group, R, sig.r, 0, null);
168             auto x = BN_new(), y = BN_new();
169             auto ctx = BN_CTX_new();
170             scope (exit)
171             {
172                 BN_free(x);
173                 BN_free(y);
174                 EC_POINT_free(R);
175             }
176             EC_POINT_get_affine_coordinates_GFp(group, R, x, y, null);
177             y.BN_bn2dec.puts;
178             y.BN_is_negative.writeln;
179             EC_POINT_set_compressed_coordinates_GFp(group, R, sig.r, 1, null);
180             EC_POINT_get_affine_coordinates_GFp(group, R, x, y, null);
181             y.BN_bn2dec.puts;
182             y.BN_is_negative.writeln;
183             result.recid = BN_is_odd(y);
184             BN_bn2bin(sig.r, result.r.ptr);
185             BN_bn2bin(sig.s, result.s.ptr);
186         }
187         return result;
188     }
189 }