1 module secp256k1; 2 import include.secp256k1_recovery; 3 import include.secp256k1; 4 import std.stdio; 5 import std.exception : enforce; 6 import std.conv : to, text; 7 import std : toHexString; 8 9 import core.stdc.stdlib : free; 10 import keccak : keccak_256, keccak256; 11 12 alias bytes = ubyte[]; 13 unittest 14 { 15 writeln("Elliptic Curve example"); 16 ubyte[32] pk; 17 pk[0 .. $] = "beb75b08049e9316d1375999c7d968f3c23fdf606b296fcdfc9a41cdd7e7347c".hexToBytes; 18 auto ec = new secp256k1(pk); 19 auto msg = keccak256(cast(ubyte[]) "abbbabbaabababababa"); 20 ec.writeln; 21 ec.sign(msg).writeln; 22 ec.address.toHexString.writeln(" -- address"); 23 ec.sign(msg).ecRecover(msg).toHexString.writeln("-- recovered address"); 24 foreach (i; 0 .. 500) 25 { 26 auto sign = ec.sign(msg); 27 auto recAddr = sign.ecRecover(msg); 28 assert(recAddr == ec.address, text(sign, recAddr)); 29 } 30 assert(ec.verify(msg, ec.sign(msg))); 31 } 32 33 bytes hexToBytes(string s) 34 { 35 import std : chunks, map, array, startsWith, replace; 36 import std.range : padLeft; 37 38 s = s.replace("_", "").replace(" ", ""); 39 if (s.startsWith(`0x`)) 40 s = s[2 .. $]; 41 42 return s.padLeft('0', s.length + s.length % 2).chunks(2).map!q{a.parse!ubyte(16)}.array; 43 } 44 45 struct Signature 46 { 47 int recid; 48 ubyte[32] r; 49 ubyte[32] s; 50 } 51 52 class secp256k1 53 { 54 private secp256k1_context* ctx; 55 ubyte[32] secKey; 56 this(ubyte[32] key) 57 { 58 secKey = key; 59 ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); 60 61 } 62 63 Signature sign(ubyte[] data) 64 { 65 ubyte[32] msgHash = keccak256(data); 66 secp256k1_ecdsa_recoverable_signature signature; 67 ctx.secp256k1_ecdsa_sign_recoverable(&signature, msgHash.ptr, secKey.ptr, null, null); 68 return Signature(signature.data[64], signature.data[0 .. 32], signature.data[32 .. 64]); 69 } 70 71 bool verify(ubyte[] data, Signature sig) 72 { 73 secp256k1_ecdsa_signature cSig; 74 75 auto msgHash = keccak256(data); 76 cSig.data[0 .. 32] = sig.r; 77 cSig.data[32 .. 64] = sig.s; 78 secp256k1_pubkey pubKey; 79 assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr)); 80 return cast(bool) ctx.secp256k1_ecdsa_verify(&cSig, msgHash.ptr, &pubKey); 81 82 } 83 84 ubyte[20] address() @property const 85 { 86 import std.algorithm : reverse; 87 88 secp256k1_pubkey pubKey; 89 assert(secp256k1_ec_pubkey_create(ctx, &pubKey, secKey.ptr)); 90 return pubKey.toAddress; 91 } 92 93 ~this() 94 { 95 secp256k1_context_destroy(ctx); 96 } 97 } 98 99 ubyte[20] toAddress(secp256k1_pubkey pubKey) 100 { 101 import std.algorithm : reverse; 102 103 pubKey.data[0 .. 32].reverse; 104 pubKey.data[32 .. $].reverse; 105 return keccak256(pubKey.data[])[12 .. $]; 106 107 } 108 109 ubyte[20] ecRecover(Signature sig, ubyte[] msg) 110 { 111 auto ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); 112 secp256k1_pubkey pubKey; 113 secp256k1_ecdsa_recoverable_signature cSig; 114 cSig.data[64] = cast(ubyte) sig.recid; 115 cSig.data[0 .. 32] = sig.r; 116 cSig.data[32 .. 64] = sig.s; 117 auto msgHash = keccak256(msg); 118 ctx.secp256k1_ecdsa_recover(&pubKey, &cSig, msgHash.ptr); 119 return toAddress(pubKey); 120 121 }