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 }