Skip to content

Instantly share code, notes, and snippets.

@CrackerHax
Last active June 21, 2023 13:59
Show Gist options
  • Select an option

  • Save CrackerHax/a7eae0ab0bea0184cdeaef35f79f1386 to your computer and use it in GitHub Desktop.

Select an option

Save CrackerHax/a7eae0ab0bea0184cdeaef35f79f1386 to your computer and use it in GitHub Desktop.
C++ ethereum ecRecover function using libsecp256k1 (with recovery module enabled) and keccak-tiny. Returns public eth address from signed message. Used to prove ownership of an eth address for logging in, etc.
#include "keccak-tiny.h"
#include "secp256k1.h"
#include "secp256k1_recovery.h"
std::string bytes_to_hex_string(const uint8_t *str, const uint64_t s)
{
std::ostringstream ret;
for (size_t i = 0; i < s; ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << std::nouppercase << (int) str[i];
return ret.str();
}
std::string hex_to_string(const std::string& input)
{
static const char* const lut = "0123456789abcdef";
size_t len = input.length();
if (len & 1) throw std::invalid_argument("odd length");
std::string output;
output.reserve(len / 2);
for (size_t i = 0; i < len; i += 2)
{
char a = input[i];
const char* p = std::lower_bound(lut, lut + 16, a);
if (*p != a) throw std::invalid_argument("not a hex digit");
char b = input[i + 1];
const char* q = std::lower_bound(lut, lut + 16, b);
if (*q != b) throw std::invalid_argument("not a hex digit");
output.push_back(((p - lut) << 4) | (q - lut));
}
return output;
}
std::string ecrecover(std::string sig, std::string msg) // hex-encoded sig, plain text msg
{
std::string _sig = hex_to_string(sig.substr(2)); // strip 0x
if(_sig.size() != 65)
return ("0x00000000000000000000000000000000");
int v = _sig[64];
_sig = _sig.substr(0,64);
if(v>3)
v-=27;
auto* ctx = secp256k1_context_create( SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY );
secp256k1_ecdsa_recoverable_signature rawSig;
if(!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rawSig, (unsigned char*)_sig.data(), v))
return ("0x00000000000000000000000000000000");
std::array<uint8_t,32> hash;
keccak_256(hash.data(), hash.size(), (unsigned char*)msg.data(), msg.length()); // hash message
msg = string("\x19")+"Ethereum Signed Message:\n32"+string(hash.begin(),hash.end()); // wrap message hash
keccak_256(hash.data(), hash.size(), (unsigned char*)msg.data(), msg.length()); // hash wrapped message hash
secp256k1_pubkey rawPubkey;
if(!secp256k1_ecdsa_recover(ctx, &rawPubkey, &rawSig, hash.data())) // 32 bit hash
return ("0x00000000000000000000000000000000");
std::array<uint8_t,65> pubkey;
size_t biglen = 65;
secp256k1_ec_pubkey_serialize(ctx, pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED);
std::string out = std::string(pubkey.begin(),pubkey.end()).substr(1);
keccak_256(hash.data(), hash.size(), (const unsigned char*)out.data(), out.length());
return("0x"+bytes_to_hex_string(hash.data(),hash.size()).substr(24));
}
@dtaghavi
Copy link
Copy Markdown

dtaghavi commented Mar 16, 2022

Would you be willing to share your included files? Got a project I'm working on and been struggling to implement this functionality.
I can't find this tiny-keccak or the secp256k files.

@CrackerHax
Copy link
Copy Markdown
Author

CrackerHax commented Aug 6, 2022

Would you be willing to share your included files? Got a project I'm working on and been struggling to implement this functionality. I can't find this tiny-keccak or the secp256k files.

I believe these are the ones I used:
https://github.com/urbit/secp256k1
https://github.com/coruus/keccak-tiny

@CrackerHax
Copy link
Copy Markdown
Author

CrackerHax commented Oct 11, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment