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) 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 protected bool canSign; 58 protected bool canVerify; 59 protected ubyte[32] secKey; 60 protected ubyte[64] pubKey; 61 62 this() 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(ubyte[32] key) 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(ubyte[64] pubKey) 90 { 91 canVerify = true; 92 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 93 this.pubKey = pubKey; 94 } 95 96 Signature sign(ubyte[] data) 97 { 98 canSign.enforce("this key cannot sign: secKey not inited"); 99 ubyte[32] msgHash = keccak256(data); 100 secp256k1_ecdsa_recoverable_signature signature; 101 ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null); 102 103 signature.data[].reverse; 104 return Signature(signature.data[0], signature.data[33 .. 65], signature.data[1 .. 33]); 105 } 106 107 bool verify(ubyte[] data, Signature sig) 108 { 109 secp256k1_ecdsa_signature cSig; 110 111 auto msgHash = keccak256(data); 112 cSig.data[32 .. 64] = sig.r; 113 cSig.data[0 .. 32] = sig.s; 114 cSig.data[].reverse; 115 return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, 116 cast(secp256k1_pubkey*)&pubKey); 117 118 } 119 120 ubyte[20] address() @property const 121 { 122 return pubKey.secp256k1_pubkey.toAddress; 123 } 124 125 ~this() 126 { 127 secp256k1_context_destroy(ctx); 128 } 129 } 130 131 ubyte[20] toAddress(secp256k1_pubkey pubKey) 132 { 133 import std.algorithm : reverse; 134 135 pubKey.data[0 .. 32].reverse; 136 pubKey.data[32 .. $].reverse; 137 return keccak256(pubKey.data[])[12 .. $]; 138 139 } 140 141 ubyte[20] ecRecover(Signature sig, ubyte[] msg) 142 { 143 auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 144 secp256k1_pubkey pubKey; 145 secp256k1_ecdsa_recoverable_signature cSig; 146 cSig.data[0] = cast(ubyte) sig.recid; 147 cSig.data[33 .. 65] = sig.r; 148 cSig.data[1 .. 33] = sig.s; 149 cSig.data[].reverse; 150 auto msgHash = keccak256(msg); 151 ctx.secp256k1_ecdsa_recover(cast(secp256k1_pubkey*)&pubKey, &cSig, msgHash.ptr); 152 return toAddress(pubKey); 153 154 } 155 156 unittest 157 { 158 import std.stdio; 159 160 writeln("Leading zero seach test"); 161 foreach (i; 0 .. 10_000) 162 { 163 auto a = new secp256k1; 164 auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa"); 165 auto sig = a.sign(msg); 166 assert(a.verify(msg, sig)); 167 auto addr = a.address.toHexString; 168 if (addr.leadingZeros > 2) 169 { 170 addr.writeln; 171 } 172 } 173 174 } 175 176 auto leadingZeros(T)(T s) 177 { 178 179 foreach (i, c; s) 180 { 181 if (c != '0') 182 return i; 183 } 184 return s.length; 185 }