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, 121
24     ];
25     auto ec = new secp256k1(pk);
26     ec.writeln;
27     ec.address.writeln(" --  address");
28     ec.sign!string("abababa").writeln;
29     ec.sign!string("abababa").writeln;
30 }
31 
32 class secp256k1
33 {
34     EC_KEY* ec_key;
35 
36     this(ubyte[] pk = null)
37     {
38         ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);
39         if (pk == null)
40         {
41             EC_KEY_generate_key(ec_key);
42         }
43         else
44         {
45             auto bn_pk = BN_bin2bn(pk.ptr, cast(int) pk.length, null);
46             EC_KEY_set_private_key(ec_key, bn_pk);
47             auto group = EC_KEY_get0_group(ec_key);
48             auto pub_key = EC_POINT_new(group);
49             auto ctx = BN_CTX_new();
50             scope (exit)
51             {
52                 BN_free(bn_pk);
53                 BN_CTX_free(ctx);
54                 EC_POINT_free(pub_key);
55             }
56             EC_POINT_mul(group, pub_key, bn_pk, null, null, ctx);
57             EC_KEY_set_public_key(ec_key, pub_key);
58         }
59     }
60 
61     auto sign(Result, Input)(Input data)
62     {
63         ubyte[] _data = cast(ubyte[]) data.dup;
64         auto sig = ECDSA_do_sign(_data.ptr, cast(int) _data.length, ec_key);
65         assert(ECDSA_do_verify(_data.ptr, cast(int) _data.length, sig, ec_key), "Check signature");
66         auto result = sig.ECDSA_SIG_to!Result;
67         ECDSA_SIG_free(sig);
68         return result;
69     }
70 
71     override string toString()
72     {
73         auto pkBN = EC_KEY_get0_private_key(ec_key);
74         auto cptr = BN_bn2hex(pkBN);
75         ubyte[32] pkBytes;
76         BN_bn2bin(pkBN, pkBytes.ptr);
77         string result = cptr.to!string ~ pkBytes.to!string ~ " Can sign: " ~ EC_KEY_check_key(ec_key)
78             .to!string;
79         OPENSSL_free(cptr);
80         return result;
81     }
82 
83     ~this()
84     {
85         EC_KEY_free(ec_key);
86     }
87 
88     int parity()
89     {
90         auto point = EC_KEY_get0_public_key(ec_key);
91         auto group = EC_KEY_get0_group(ec_key);
92         auto x = BN_new(), y = BN_new();
93         auto ctx = BN_CTX_new();
94         scope (exit)
95         {
96             BN_CTX_free(ctx);
97             BN_free(x);
98             BN_free(y);
99         }
100         EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx);
101         return BN_is_odd(y);
102     }
103 
104     auto publicKey()
105     {
106         auto pubKey = EC_POINT_point2bn(ec_key.EC_KEY_get0_group, ec_key.EC_KEY_get0_public_key,
107                 point_conversion_form_t.POINT_CONVERSION_UNCOMPRESSED, null, null);
108         ubyte[65] data;
109         BN_bn2bin(pubKey, data.ptr);
110 
111         scope (exit)
112         {
113             BN_free(pubKey);
114         }
115         return data;
116     }
117 
118     ubyte[20] address()
119     {
120         ubyte[32] hash;
121         auto pubKey = publicKey[1 .. $];
122         keccak_256(hash.ptr, hash.length, pubKey.ptr, pubKey.length);
123         return hash[12 .. $];
124     }
125 }
126 
127 auto ECDSA_SIG_to(Result)(ECDSA_SIG* sig)
128 {
129     static if (is(Result == string))
130     {
131         auto rptr = BN_bn2hex(sig.r);
132         auto sptr = BN_bn2hex(sig.s);
133         auto result = rptr.to!string ~ BN_bn2hex(sig.s).to!string;
134         scope (exit)
135         {
136             free(rptr);
137             free(sptr);
138         }
139     }
140     else
141     {
142         ubyte[32] r;
143         ubyte[32] s;
144         BN_bn2bin(sig.r, r.ptr);
145         BN_bn2bin(sig.s, s.ptr);
146         auto result = r[] ~ s[];
147     }
148     return result;
149 }