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 }