1 module secp256k1; 2 3 import std.stdio; 4 import std.exception : enforce; 5 import std.conv : to, text; 6 import std : toHexString; 7 import std.algorithm : reverse; 8 9 import include.secp256k1_recovery; 10 import include.secp256k1; 11 12 import keccak : keccak256; 13 14 alias bytes = ubyte[]; 15 unittest 16 { 17 writeln("Elliptic Curve example"); 18 ubyte[32] pk; 19 pk[0 .. $] = "beb75b08049e9316d1375999c7d968f3c23fdf606b296fcdfc9a41cdd7e7347c".hexToBytes; 20 auto ec = new secp256k1(pk); 21 auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa"); 22 ec.writeln; 23 ec.sign(msg).writeln; 24 ec.address.toHexString.writeln(" -- address"); 25 ec.sign(msg).ecRecover(msg).toHexString.writeln(" -- recovered address"); 26 foreach (i; 0 .. 500) 27 { 28 auto sign = ec.sign(msg); 29 auto recAddr = sign.ecRecover(msg); 30 assert(recAddr == ec.address, text(sign, recAddr)); 31 } 32 assert(ec.verify(msg, ec.sign(msg))); 33 } 34 35 private bytes hexToBytes(string s) pure 36 { 37 import std : chunks, map, array, startsWith, replace; 38 import std.range : padLeft; 39 40 s = s.replace("_", "").replace(" ", ""); 41 if (s.startsWith(`0x`)) 42 s = s[2 .. $]; 43 44 return s.padLeft('0', s.length + s.length % 2).chunks(2).map!q{a.parse!ubyte(16)}.array; 45 } 46 47 struct Signature 48 { 49 int recid; 50 ubyte[32] r; 51 ubyte[32] s; 52 } 53 54 class secp256k1 55 { 56 private secp256k1_context* ctx; 57 bool canSign; 58 bool canVerify; 59 ubyte[32] secKey; 60 ubyte[64] pubKey; 61 62 this() @trusted 63 { 64 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); 65 do 66 { 67 import random = std.random; 68 69 random.uniform!"[]"(ulong.min, ulong.max); 70 static immutable secKeySize = 32 / ulong.sizeof; 71 foreach (ref e; cast(ulong[secKeySize]) secKey) 72 { 73 e = random.uniform(ulong.min, ulong.max); 74 } 75 } 76 while (!secp256k1_ec_seckey_verify(ctx, secKey.ptr)); 77 canSign = canVerify = true; 78 assert(secp256k1_ec_pubkey_create(ctx, cast(secp256k1_pubkey*)&pubKey, secKey.ptr)); 79 } 80 81 this(const ubyte[32] key) pure nothrow @safe @nogc 82 { 83 canSign = canVerify = true; 84 secKey = key; 85 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); 86 assert(secp256k1_ec_pubkey_create(ctx, cast(secp256k1_pubkey*)&pubKey, secKey.ptr)); 87 } 88 89 this(const ubyte[64] pubKey) pure nothrow @safe @nogc 90 { 91 canVerify = true; 92 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 93 this.pubKey = pubKey; 94 } 95 96 Signature sign(const ubyte[] data) pure @trusted 97 { 98 ubyte[32] msgHash = keccak256(data); 99 return signHash(msgHash); 100 } 101 102 Signature signHash(const ubyte[32] msgHash) pure @trusted 103 { 104 canSign.enforce("this key cannot sign: secKey not inited"); 105 secp256k1_ecdsa_recoverable_signature signature; 106 ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null); 107 108 signature.data[].reverse; 109 return Signature(signature.data[0], signature.data[33 .. 65], signature.data[1 .. 33]); 110 } 111 112 bool verify(const ubyte[] data, const Signature sig) pure nothrow @nogc @trusted 113 114 { 115 secp256k1_ecdsa_signature cSig; 116 117 auto msgHash = keccak256(data); 118 cSig.data[32 .. 64] = sig.r; 119 cSig.data[0 .. 32] = sig.s; 120 cSig.data[].reverse; 121 return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, 122 cast(secp256k1_pubkey*)&pubKey); 123 124 } 125 126 ubyte[20] address() @property const pure @nogc @safe nothrow 127 { 128 return pubKey.secp256k1_pubkey.toAddress; 129 } 130 131 ~this() 132 { 133 secp256k1_context_destroy(ctx); 134 } 135 } 136 137 ubyte[20] toAddress(secp256k1_pubkey pubKey) pure nothrow @nogc @safe 138 { 139 import std.algorithm : reverse; 140 141 pubKey.data[0 .. 32].reverse; 142 pubKey.data[32 .. $].reverse; 143 return keccak256(pubKey.data[])[12 .. $]; 144 145 } 146 147 ubyte[20] ecRecover(Signature sig, ubyte[] msg) pure nothrow @nogc @trusted 148 { 149 auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 150 secp256k1_pubkey pubKey; 151 secp256k1_ecdsa_recoverable_signature cSig; 152 cSig.data[0] = cast(ubyte) sig.recid; 153 cSig.data[33 .. 65] = sig.r; 154 cSig.data[1 .. 33] = sig.s; 155 cSig.data[].reverse; 156 auto msgHash = keccak256(msg); 157 ctx.secp256k1_ecdsa_recover(cast(secp256k1_pubkey*)&pubKey, &cSig, msgHash.ptr); 158 return toAddress(pubKey); 159 160 } 161 162 unittest 163 { 164 import std.stdio; 165 166 writeln("Leading zero seach test"); 167 foreach (i; 0 .. 10_000) 168 { 169 auto a = new secp256k1; 170 auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa"); 171 auto sig = a.sign(msg); 172 assert(a.verify(msg, sig)); 173 auto addr = a.address.toHexString; 174 if (addr.leadingZeros > 2) 175 { 176 addr.writeln; 177 } 178 } 179 180 } 181 182 auto leadingZeros(T)(T s) pure nothrow @nogc @safe 183 { 184 185 foreach (i, c; s) 186 { 187 if (c != '0') 188 return i; 189 } 190 return s.length; 191 }