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 
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.sign!string("abababa").writeln;
28 }
29 
30 class secp256k1
31 {
32     EC_KEY* ec_key;
33 
34     this(ubyte[] pk = null)
35     {
36         ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);
37         if (pk == null)
38         {
39             EC_KEY_generate_key(ec_key);
40         }
41         else
42         {
43             auto bn_pk = BN_bin2bn(pk.ptr, cast(int) pk.length, null);
44             EC_KEY_set_private_key(ec_key, bn_pk);
45             auto group = EC_KEY_get0_group(ec_key);
46             auto pub_key = EC_POINT_new(group);
47             auto ctx = BN_CTX_new();
48             scope (exit)
49             {
50                 BN_free(bn_pk);
51                 BN_CTX_free(ctx);
52                 EC_POINT_free(pub_key);
53             }
54             EC_POINT_mul(group, pub_key, bn_pk, null, null, ctx);
55             EC_KEY_set_public_key(ec_key, pub_key);
56         }
57     }
58 
59     auto sign(Result, Input)(Input data)
60     {
61         ubyte[] _data = cast(ubyte[]) data.dup;
62         auto sig = ECDSA_do_sign(_data.ptr, cast(int) _data.length, ec_key);
63         assert(ECDSA_do_verify(_data.ptr, cast(int) _data.length, sig, ec_key), "Check signature");
64         static if (is(Result == string))
65         {
66             auto rptr = BN_bn2hex(sig.r);
67             auto sptr = BN_bn2hex(sig.s);
68             auto result = rptr.to!string ~ BN_bn2hex(sig.s).to!string;
69             scope (exit)
70             {
71                 free(rptr);
72                 free(sptr);
73             }
74         }
75         else
76         {
77             ubyte[32] r;
78             ubyte[32] s;
79             BN_bn2bin(sig.r, r.ptr);
80             BN_bn2bin(sig.s, s.ptr);
81             auto result = r[] ~ s[];
82         }
83         ECDSA_SIG_free(sig);
84         return result;
85     }
86 
87     override string toString()
88     {
89         auto pkBN = EC_KEY_get0_private_key(ec_key);
90         auto cptr = BN_bn2hex(pkBN);
91         ubyte[32] pkBytes;
92         BN_bn2bin(pkBN, pkBytes.ptr);
93         string result = cptr.to!string ~ pkBytes.to!string ~ " Can sign: " ~ EC_KEY_check_key(ec_key)
94             .to!string;
95         free(cptr);
96         return result;
97     }
98 
99     ~this()
100     {
101         EC_KEY_free(ec_key);
102     }
103 
104     int parity()
105     {
106         auto point = EC_KEY_get0_public_key(ec_key);
107         auto group = EC_KEY_get0_group(ec_key);
108         auto x = BN_new(), y = BN_new();
109         auto ctx = BN_CTX_new();
110         scope (exit)
111         {
112             BN_CTX_free(ctx);
113             BN_free(x);
114             BN_free(y);
115         }
116         EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx);
117         return BN_is_odd(y);
118     }
119 }