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