#include

#include

#include

#include

static void die(const char* m){ std::cerr << m << "\n"; std::exit(1); }

static std::vector hex2bin(const std::string& hex){

std::vector out(hex.size()/2);

size_t bin_len=0;

if (sodium_hex2bin(out.data(), out.size(), hex.c_str(), hex.size(), nullptr, &bin_len, nullptr) != 0)

die("bad hex");

out.resize(bin_len);

return out;

}

int main(int argc, char** argv){

if (sodium_init() < 0) die("sodium_init failed");

bool decrypt=false;

int argi=1;

if (argc>=2 && std::string(argv[1])=="-d"){ decrypt=true; argi=2; }

if ((decrypt && argc-argi!=2) || (!decrypt && argc-argi!=2)){

std::cerr << "Usage:\n Encrypt: " << argv[0] << " \n"

<< " Decrypt: " << argv[0] << " -d \n";

return 1;

}

std::string a = argv[argi];

std::string priv_hex = argv[argi+1];

auto priv = hex2bin(priv_hex);

if (priv.size()!=32) die("privkey must be 32 bytes (64 hex chars)");

// Key derivation: k = BLAKE2b("BTC-LOVE-1" || privkey)

const std::string ctx = "BTC-LOVE-1";

std::vector k(crypto_aead_xchacha20poly1305_ietf_KEYBYTES);

crypto_generichash_state st;

crypto_generichash_init(&st, nullptr, 0, k.size());

crypto_generichash_update(&st, reinterpret_cast(ctx.data()), ctx.size());

crypto_generichash_update(&st, priv.data(), priv.size());

crypto_generichash_final(&st, k.data(), k.size());

if (!decrypt){

const size_t NONCE_LEN = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES; // 24

std::vector nonce(NONCE_LEN);

randombytes_buf(nonce.data(), nonce.size());

const unsigned char* ad = reinterpret_cast("Chief Hodler");

const size_t ad_len = 12;

const unsigned char* msg = reinterpret_cast(a.data());

size_t mlen = a.size();

std::vector ct(mlen + crypto_aead_xchacha20poly1305_ietf_ABYTES);

unsigned long long clen=0;

if (crypto_aead_xchacha20poly1305_ietf_encrypt(

ct.data(), &clen, msg, mlen, ad, ad_len, nullptr,

nonce.data(), k.data()) != 0) die("encrypt failed");

ct.resize(clen);

// package: nonce || ct

std::vector pack; pack.reserve(nonce.size()+ct.size());

pack.insert(pack.end(), nonce.begin(), nonce.end());

pack.insert(pack.end(), ct.begin(), ct.end());

// base64

size_t b64_len = sodium_base64_ENCODED_LEN(pack.size(), sodium_base64_VARIANT_ORIGINAL);

std::vector b64(b64_len);

sodium_bin2base64(b64.data(), b64.size(),

pack.data(), pack.size(),

sodium_base64_VARIANT_ORIGINAL);

std::cout << b64.data() << "\n";

} else {

// input is base64 pack

const std::string b64 = a;

std::vector pack(b64.size()); size_t pack_len=0;

if (sodium_base642bin(pack.data(), pack.size(), b64.c_str(), b64.size(),

nullptr, &pack_len, nullptr, sodium_base64_VARIANT_ORIGINAL) != 0)

die("bad base64");

pack.resize(pack_len);

const size_t NONCE_LEN = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;

if (pack.size() < NONCE_LEN + crypto_aead_xchacha20poly1305_ietf_ABYTES) die("cipher too short");

std::vector nonce(pack.begin(), pack.begin()+NONCE_LEN);

std::vector ct(pack.begin()+NONCE_LEN, pack.end());

const unsigned char* ad = reinterpret_cast("Chief Hodler");

const size_t ad_len = 12;

std::vector pt(ct.size()); unsigned long long plen=0;

if (crypto_aead_xchacha20poly1305_ietf_decrypt(

pt.data(), &plen, nullptr, ct.data(), ct.size(),

ad, ad_len, nonce.data(), k.data()) != 0)

die("decryption failed (wrong key or corrupted data)");

pt.resize(plen);

std::cout.write(reinterpret_cast(pt.data()), pt.size());

std::cout << "\n";

}

return 0;

}

Reply to this note

Please Login to reply.

Discussion

No replies yet.