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 }