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         auto ctx = BN_CTX_new;
77         EC_POINT* R = EC_POINT_new(group);
78         scope (exit)
79         {
80             foreach (bn; [order, k, kinv, rp, Rx, Ry])
81                 bn.BN_free;
82             ctx.BN_CTX_free;
83             EC_POINT_free(R);
84             ECDSA_SIG_free(sig);
85         }
86         handleErr!EC_GROUP_get_order(group, order, ctx);
87         handleErr!ECDSA_sign_setup(ec_key, null, &kinv, &rp);
88         handleErr!BN_mod_inverse(k, kinv, order, ctx);
89         assert(EC_POINT_mul(group, R, k, null, null, ctx));
90         handleErr!EC_POINT_get_affine_coordinates_GFp(group, R, Rx, Ry, ctx);
91         sig = ECDSA_do_sign_ex(_data.ptr, cast(int) _data.length, kinv, rp, ec_key);
92 
93         assert(ECDSA_do_verify(_data.ptr, cast(int) _data.length, sig, ec_key), "Check signature");
94         assert(0 == BN_cmp(sig.r, Rx), "Rx == r");
95 
96         Signature result;
97         result.recid = BN_is_odd(Ry);
98         BN_bn2bin(sig.r, result.r.ptr);
99         BN_bn2bin(sig.s, result.s.ptr);
100         return result;
101     }
102 
103     override string toString()
104     {
105         auto pkBN = EC_KEY_get0_private_key(ec_key);
106         auto cptr = BN_bn2hex(pkBN);
107         ubyte[32] pkBytes;
108         BN_bn2bin(pkBN, pkBytes.ptr);
109         string result = cptr.to!string ~ pkBytes.to!string ~ " Can sign: " ~ EC_KEY_check_key(ec_key)
110             .to!string;
111         OPENSSL_free(cptr);
112         return result;
113     }
114 
115     ~this()
116     {
117         EC_KEY_free(ec_key);
118     }
119 
120     auto publicKey()
121     {
122         auto pubKey = EC_POINT_point2bn(ec_key.EC_KEY_get0_group, ec_key.EC_KEY_get0_public_key,
123                 point_conversion_form_t.POINT_CONVERSION_UNCOMPRESSED, null, null);
124         ubyte[65] data;
125         BN_bn2bin(pubKey, data.ptr);
126 
127         scope (exit)
128         {
129             BN_free(pubKey);
130         }
131         return data;
132     }
133 
134     ubyte[20] address()
135     {
136         ubyte[32] hash;
137         auto pubKey = publicKey[1 .. $] ~ [];
138         keccak_256(hash.ptr, hash.length, pubKey.ptr, pubKey.length);
139         return hash[12 .. $];
140     }
141 
142 }
143 
144 private auto handleErr(alias f, ARGS...)(ARGS argv)
145 {
146     import std.traits : fullyQualifiedName;
147 
148     enforce(f(argv), fullyQualifiedName!f ~ " crashed");
149 }