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 ubyte[32] secKey; 58 this(ubyte[32] key) 59 { 60 secKey = key; 61 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); 62 63 } 64 65 Signature sign(ubyte[] data) 66 { 67 ubyte[32] msgHash = keccak256(data); 68 secp256k1_ecdsa_recoverable_signature signature; 69 ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null); 70 71 signature.data[].reverse; 72 return Signature(signature.data[0], signature.data[33 .. 65], signature.data[1 .. 33]); 73 } 74 75 bool verify(ubyte[] data, Signature sig) 76 { 77 secp256k1_ecdsa_signature cSig; 78 79 auto msgHash = keccak256(data); 80 cSig.data[32 .. 64] = sig.r; 81 cSig.data[0 .. 32] = sig.s; 82 cSig.data[].reverse; 83 secp256k1_pubkey pubKey; 84 assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr)); 85 return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, &pubKey); 86 87 } 88 89 ubyte[20] address() @property const 90 { 91 secp256k1_pubkey pubKey; 92 assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr)); 93 return pubKey.toAddress; 94 } 95 96 ~this() 97 { 98 secp256k1_context_destroy(ctx); 99 } 100 } 101 102 ubyte[20] toAddress(secp256k1_pubkey pubKey) 103 { 104 import std.algorithm : reverse; 105 106 pubKey.data[0 .. 32].reverse; 107 pubKey.data[32 .. $].reverse; 108 return keccak256(pubKey.data[])[12 .. $]; 109 110 } 111 112 ubyte[20] ecRecover(Signature sig, ubyte[] msg) 113 { 114 auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 115 secp256k1_pubkey pubKey; 116 secp256k1_ecdsa_recoverable_signature cSig; 117 cSig.data[0] = cast(ubyte) sig.recid; 118 cSig.data[33 .. 65] = sig.r; 119 cSig.data[1 .. 33] = sig.s; 120 cSig.data[].reverse; 121 auto msgHash = keccak256(msg); 122 ctx.secp256k1_ecdsa_recover(&pubKey, &cSig, msgHash.ptr); 123 return toAddress(pubKey); 124 125 }