diff --git a/phoenixcoin-qt.pro b/phoenixcoin-qt.pro index f5aebd03..233a2a4f 100644 --- a/phoenixcoin-qt.pro +++ b/phoenixcoin-qt.pro @@ -299,9 +299,8 @@ SOURCES += src/qt/phoenixcoin.cpp \ src/noui.cpp \ src/neoscrypt.c \ src/neoscrypt_asm.S \ - src/ecies/secure.c \ - src/ecies/ecies.c \ - src/ecies/kdf.c \ + src/ecies/secure.cpp \ + src/ecies/ecies.cpp \ src/ntp.cpp \ src/qt/walletmodeltransaction.cpp \ src/qt/coincontrol.cpp @@ -384,9 +383,8 @@ macx:QMAKE_CXXFLAGS_THREAD += -pthread # Set libraries and includes at end, to use platform-defined defaults if not overridden INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$QRENCODE_INCLUDE_PATH LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) -LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX +LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX -lsecp256k1 # -lgdi32 has to happen after -lcrypto (see #681) win32:LIBS += -lws2_32 -lmswsock -lshlwapi -lole32 -loleaut32 -luuid -lgdi32 -LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_LIB_SUFFIX - +LIBS += -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_LIB_SUFFIX system($$QMAKE_LRELEASE -silent $$_PRO_FILE_) diff --git a/src/Makefile.linux b/src/Makefile.linux index af4b0247..152baaab 100644 --- a/src/Makefile.linux +++ b/src/Makefile.linux @@ -49,7 +49,6 @@ $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) ifdef DYNAMIC LIBS += \ - -l:libboost_system.so$(BOOST_LIB_SUFFIX) \ -l:libboost_filesystem.so$(BOOST_LIB_SUFFIX) \ -l:libboost_program_options.so$(BOOST_LIB_SUFFIX) \ -l:libboost_thread.so$(BOOST_LIB_SUFFIX) \ @@ -61,7 +60,6 @@ TESTDEFS += -DBOOST_TEST_DYN_LINK TESTLIBS += -l:libboost_unit_test_framework.so$(BOOST_LIB_SUFFIX) else ifdef STATIC LIBS += \ - -l:libboost_system$(BOOST_LIB_SUFFIX).a \ -l:libboost_filesystem$(BOOST_LIB_SUFFIX).a \ -l:libboost_program_options$(BOOST_LIB_SUFFIX).a \ -l:libboost_thread$(BOOST_LIB_SUFFIX).a \ @@ -72,7 +70,6 @@ LIBS += \ TESTLIBS += -l:libboost_unit_test_framework$(BOOST_LIB_SUFFIX).a else LIBS += \ - -lboost_system$(BOOST_LIB_SUFFIX) \ -lboost_filesystem$(BOOST_LIB_SUFFIX) \ -lboost_program_options$(BOOST_LIB_SUFFIX) \ -lboost_thread$(BOOST_LIB_SUFFIX) \ @@ -98,7 +95,7 @@ ifeq (${STATIC}, all) LIBS += -Wl,-Bstatic endif -LIBS += -lz -ldl -lpthread -lrt +LIBS += -lz -ldl -lpthread -lrt -lsecp256k1 OBJS = \ obj/addrman.o \ @@ -119,6 +116,7 @@ OBJS = \ obj/rpcblockchain.o \ obj/rpccrypto.o \ obj/rpcdump.o \ + obj/rpccrypto.o \ obj/rpcmain.o \ obj/rpcmining.o \ obj/rpcnet.o \ @@ -133,7 +131,6 @@ OBJS = \ obj/neoscrypt.o \ obj/neoscrypt_asm.o \ obj/ecies.o \ - obj/kdf.o \ obj/secure.o all: phoenixcoind @@ -161,14 +158,11 @@ obj/neoscrypt.o: neoscrypt.c obj/neoscrypt_asm.o: neoscrypt_asm.S $(CC) -c -DNEOSCRYPT_SHA256 -DNEOSCRYPT_ASM -DNEOSCRYPT_OPT -o $@ $^ -obj/ecies.o: ecies/ecies.c - $(CC) $(CFLAGS) $(addprefix -I,$(CURDIR)/ecies $(DEPSDIR)/include $(OPENSSL_INCLUDE_PATH)) -c -o $@ $^ - -obj/secure.o: ecies/secure.c - $(CC) $(CFLAGS) $(addprefix -I,$(CURDIR)/ecies $(DEPSDIR)/include $(OPENSSL_INCLUDE_PATH)) -c -o $@ $^ +obj/ecies.o: ecies/ecies.cpp + $(CXX) $(CXXFLAGS) $(DEFS) $(INCS) -c $< -o $@ -obj/kdf.o: ecies/kdf.c - $(CC) $(CFLAGS) $(addprefix -I,$(CURDIR)/ecies $(DEPSDIR)/include $(OPENSSL_INCLUDE_PATH)) -c -o $@ $^ +obj/secure.o: ecies/secure.cpp + $(CXX) $(CXXFLAGS) $(DEFS) $(INCS) -c $< -o $@ phoenixcoind: $(OBJS:obj/%=obj/%) $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) diff --git a/src/ecies/ecies.c b/src/ecies/ecies.c deleted file mode 100644 index a060d06b..00000000 --- a/src/ecies/ecies.c +++ /dev/null @@ -1,417 +0,0 @@ -/** - * @file /cryptron/ecies.c - * - * @brief ECIES encryption/decryption functions. - * - * $Author: Ladar Levison $ - * $Website: http://lavabit.com $ - * $Date: 2010/08/06 06:02:03 $ - * $Revision: a51931d0f81f6abe29ca91470931d41a374508a7 $ - * - * I hereby place the attached code in the public domain. - * As such it comes without any warranty regarding its merchantability or - * fitness for a particular purpose. Please use it at your own risk. - * - * Optimised by John Doering - * - */ - -#include /* for memset() */ - -#include "ecies.h" - -static EC_KEY *ecies_key_create(const EC_KEY *user, char *error) { - const EC_GROUP *group; - EC_KEY *key = NULL; - - if(!(key = EC_KEY_new())) { - sprintf(error, "EC_KEY_new() failed"); - return(NULL); - } - - if(!(group = EC_KEY_get0_group(user))) { - sprintf(error, "EC_KEY_get0_group() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(EC_KEY_set_group(key, group) != 1) { - sprintf(error, "EC_KEY_set_group() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(EC_KEY_generate_key(key) != 1) { - sprintf(error, "EC_KEY_generate_key() failed"); - EC_KEY_free(key); - return(NULL); - } - - return(key); -} - -static EC_KEY *ecies_key_create_public_octets(EC_KEY *user, unsigned char *octets, - size_t length, char *error) { - EC_KEY *key = NULL; - EC_POINT *point = NULL; - const EC_GROUP *group = NULL; - - if(!(key = EC_KEY_new())) { - sprintf(error, "EC_KEY_new() failed"); - return(NULL); - } - - if(!(group = EC_KEY_get0_group(user))) { - sprintf(error, "EC_KEY_get0_group() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(EC_KEY_set_group(key, group) != 1) { - sprintf(error, "EC_KEY_set_group() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(!(point = EC_POINT_new(group))) { - sprintf(error, "EC_POINT_new() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(EC_POINT_oct2point(group, point, octets, length, NULL) != 1) { - sprintf(error, "EC_POINT_oct2point() failed"); - EC_KEY_free(key); - return(NULL); - } - - if(EC_KEY_set_public_key(key, point) != 1) { - sprintf(error, "EC_KEY_set_public_key() failed"); - EC_POINT_free(point); - EC_KEY_free(key); - return(NULL); - } - - EC_POINT_free(point); - - if(EC_KEY_check_key(key) != 1) { - sprintf(error, "EC_KEY_check_key() failed"); - EC_KEY_free(key); - return(NULL); - } - - return(key); -} - -secure_t *ecies_encrypt(const ecies_ctx_t *ctx, const unsigned char *data, size_t length, - char *error) { - unsigned int output_length; - unsigned int status = 1; - - if(!ctx || !data || !length) { - sprintf(error, "Input NULL pointer detected"); - return(NULL); - } - - secure_t *cryptex = NULL; - - const size_t block_length = EVP_CIPHER_block_size(ctx->cipher); - - if(!block_length || (block_length > EVP_MAX_BLOCK_LENGTH)) { - sprintf(error, "Invalid block size"); - return(NULL); - } - - if(!(cryptex = secure_alloc(ctx->stored_key_length, EVP_MD_size(ctx->md), - length + (length % block_length ? (block_length - (length % block_length)) : 0)))) { - sprintf(error, "Unable to allocate a buffer for encrypted data"); - return(NULL); - } - - /* Stage 1: envelope key creation */ - - const size_t key_len = EVP_CIPHER_key_length(ctx->cipher) + EVP_MD_size(ctx->md); - unsigned char envelope_key[key_len]; - - const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; - unsigned char ecdh_temp_key[ecdh_key_len]; - - EC_KEY *ephemeral = NULL; - - if(!(ephemeral = ecies_key_create(ctx->user_key, error))) { - /* Error message has been set already */ - status = 0; - } else { - if(ECDH_compute_key(ecdh_temp_key, ecdh_key_len, - EC_KEY_get0_public_key(ctx->user_key), ephemeral, NULL) != (int)ecdh_key_len) { - sprintf(error, "ECDH_compute_key() failed"); - status = 0; - } else { - if(!ECDH_KDF_X9_62(envelope_key, key_len, ecdh_temp_key, ecdh_key_len, - 0, 0, ctx->kdf_md)) { - sprintf(error, "ECDH_KDF_X9_62() failed to stretch the key"); - status = 0; - } else { - /* Verify the public key portion of the ephemeral key */ - output_length = EC_POINT_point2oct(EC_KEY_get0_group(ephemeral), - EC_KEY_get0_public_key(ephemeral), POINT_CONVERSION_COMPRESSED, - secure_key_data(cryptex), ctx->stored_key_length, NULL); - if(output_length != ctx->stored_key_length) { - sprintf(error, "Incorrect length of the public portion of the envelope key"); - status = 0; - } - } - } - } - - if(ephemeral) EC_KEY_free(ephemeral); - - if(!status) return(NULL); - - /* Stage 2: symmetric cipher context encryption */ - - size_t encrypted_length; - - unsigned char iv[EVP_MAX_IV_LENGTH]; - unsigned char *body = secure_body_data(cryptex); - - EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new(); - if(!cipher) return(NULL); - EVP_CIPHER_CTX_init(cipher); - - memset(iv, 0, EVP_MAX_IV_LENGTH); - - if(EVP_EncryptInit_ex(cipher, ctx->cipher, NULL, envelope_key, iv) != 1) { - sprintf(error, "Initial context encryption failed"); - status = 0; - } else { - if(EVP_EncryptUpdate(cipher, body, (int *) &output_length, data, length) != 1) { - sprintf(error, "Context encryption failed"); - status = 0; - } else { - encrypted_length = output_length; - if(EVP_EncryptFinal_ex(cipher, body + encrypted_length, (int *) &output_length) != 1) { - sprintf(error, "Final context encryption failed"); - status = 0; - } else { - encrypted_length += output_length; - if(secure_body_length(cryptex) < encrypted_length) { - sprintf(error, "Cipher context overflow"); - status = 0; - } - } - } - } - - EVP_CIPHER_CTX_cleanup(cipher); - EVP_CIPHER_CTX_free(cipher); - - if(!status) { - secure_free(cryptex); - return(NULL); - } - - /* Stage 3: HMAC generation */ - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - HMAC_CTX hmac_object; - HMAC_CTX *hmac = (HMAC_CTX *) &hmac_object; - if(!hmac) return(NULL); - HMAC_CTX_init(hmac); -#else - HMAC_CTX *hmac = HMAC_CTX_new(); - if(!hmac) return(NULL); - HMAC_CTX_reset(hmac); -#endif - - const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher); - const size_t key_length = EVP_MD_size(ctx->md); - const size_t mac_length = secure_mac_length(cryptex); - - /* Generate an authenticated hash which can be used to validate the - * data during decryption */ - - if((HMAC_Init_ex(hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1) || - (HMAC_Update(hmac, secure_body_data(cryptex), secure_body_length(cryptex)) != 1) || - (HMAC_Final(hmac, secure_mac_data(cryptex), &output_length) != 1)) { - sprintf(error, "Unable to generate a HMAC tag"); - status = 0; - } else { - if(output_length != mac_length) { - sprintf(error, "HMAC tag verification failed"); - status = 0; - } - } - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - HMAC_CTX_cleanup(hmac); -#else - HMAC_CTX_reset(hmac); - HMAC_CTX_free(hmac); -#endif - - if(!status) { - secure_free(cryptex); - return(NULL); - } - - memset(envelope_key, 0, key_len); - memset(ecdh_temp_key, 0, ecdh_key_len); - - return(cryptex); -} - -unsigned char *ecies_decrypt(const ecies_ctx_t *ctx, const secure_t *cryptex, size_t *length, - char *error) { - unsigned int output_length; - unsigned int status = 1; - - if(!ctx || !cryptex || !length || !error) { - sprintf(error, "Input NULL pointer detected"); - return(NULL); - } - - /* Stage 1: envelope key recovery */ - - const size_t key_len = EVP_CIPHER_key_length(ctx->cipher) + EVP_MD_size(ctx->md); - unsigned char envelope_key[key_len]; - - const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; - unsigned char ecdh_temp_key[ecdh_key_len]; - - EC_KEY *ephemeral = NULL, *user_copy = NULL; - - if(!(user_copy = EC_KEY_new())) { - sprintf(error, "Failed to create a user key instance"); - status = 0; - } else { - if(!EC_KEY_copy(user_copy, ctx->user_key)) { - sprintf(error, "Failed to copy user key"); - status = 0; - } else { - if(!(ephemeral = ecies_key_create_public_octets(user_copy, secure_key_data(cryptex), - secure_key_length(cryptex), error))) { - /* Error message has been set already */ - status = 0; - } else { - if(ECDH_compute_key(ecdh_temp_key, ecdh_key_len, EC_KEY_get0_public_key(ephemeral), - user_copy, NULL) != (int)ecdh_key_len) { - sprintf(error, "ECDH_compute_key() failed"); - status = 0; - } else { - if(!ECDH_KDF_X9_62(envelope_key, key_len, ecdh_temp_key, ecdh_key_len, - 0, 0, ctx->kdf_md)) { - sprintf(error, "ECDH_KDF_X9_62() failed to stretch the key"); - status = 0; - } - } - } - } - } - - if(!status) { - if(ephemeral) EC_KEY_free(ephemeral); - if(user_copy) EC_KEY_free(user_copy); - return(NULL); - } - - /* Stage 2: HMAC verification */ - - unsigned char md[EVP_MAX_MD_SIZE]; - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - HMAC_CTX hmac_object; - HMAC_CTX *hmac = (HMAC_CTX *) &hmac_object; - if(!hmac) return(NULL); - HMAC_CTX_init(hmac); -#else - HMAC_CTX *hmac = HMAC_CTX_new(); - if(!hmac) return(NULL); - HMAC_CTX_reset(hmac); -#endif - - const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher); - const size_t key_length = EVP_MD_size(ctx->md); - const size_t mac_length = secure_mac_length(cryptex); - - /* Use the authenticated hash of the ciphered data to ensure it was not - * modified after being encrypted */ - - if((HMAC_Init_ex(hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1) || - (HMAC_Update(hmac, secure_body_data(cryptex), secure_body_length(cryptex)) != 1) || - (HMAC_Final(hmac, md, &output_length) != 1)) { - sprintf(error, "Unable to generate a HMAC tag"); - status = 0; - } else { - if((output_length != mac_length) || - (memcmp(md, secure_mac_data(cryptex), mac_length) != 0)) { - sprintf(error, "HMAC tag verification failed"); - status = 0; - } - } - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - HMAC_CTX_cleanup(hmac); -#else - HMAC_CTX_reset(hmac); - HMAC_CTX_free(hmac); -#endif - - if(!status) return(NULL); - - /* Stage 3: symmetric cipher context decryption */ - - size_t decrypted_length; - - unsigned char iv[EVP_MAX_IV_LENGTH]; - unsigned char *output; - - EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new(); - if(!cipher) return(NULL); - EVP_CIPHER_CTX_init(cipher); - - const size_t body_length = secure_body_length(cryptex); - - if(!(output = (unsigned char *) malloc(body_length + 1))) { - sprintf(error, "Unable to allocate a buffer for decrypted data"); - return(NULL); - } - - memset(iv, 0, EVP_MAX_IV_LENGTH); - memset(output, 0, body_length + 1); - - if(EVP_DecryptInit_ex(cipher, ctx->cipher, NULL, envelope_key, iv) != 1) { - sprintf(error, "Initial context decryption failed"); - status = 0; - } else { - if(EVP_DecryptUpdate(cipher, output, (int *) &output_length, - secure_body_data(cryptex), body_length) != 1) { - sprintf(error, "Context decryption failed"); - status = 0; - } else { - decrypted_length = output_length; - if(EVP_DecryptFinal_ex(cipher, output + decrypted_length, - (int *) &output_length) != 1) { - sprintf(error, "Final context decryption failed"); - status = 0; - } else { - decrypted_length += output_length; - } - } - } - - EVP_CIPHER_CTX_cleanup(cipher); - EVP_CIPHER_CTX_free(cipher); - - if(!status) { - free(output); - return(NULL); - } - - *length = decrypted_length; - - memset(envelope_key, 0, key_len); - memset(ecdh_temp_key, 0, ecdh_key_len); - - return(output); -} diff --git a/src/ecies/ecies.cpp b/src/ecies/ecies.cpp new file mode 100644 index 00000000..803ae403 --- /dev/null +++ b/src/ecies/ecies.cpp @@ -0,0 +1,281 @@ +// Copyright (c) 2026 sats0k +// Distributed under the MIT/X11 software licence, see the accompanying +// file LICENCE or http://opensource.org/license/mit + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ecies.h" + +using ByteVector = std::vector; + +static constexpr uint8_t ECIES_VERSION = 0x01; +static constexpr size_t PUBKEY_LEN = 65; // uncompressed secp256k1 +static constexpr size_t NONCE_LEN = 12; // GCM +static constexpr size_t TAG_LEN = 16; +static constexpr size_t KEY_LEN = 32; + +/* ------------------------------------------------ */ +/* RAII helpers */ +/* ------------------------------------------------ */ + +struct Pkey { + EVP_PKEY* p = nullptr; + ~Pkey() { + if (p) EVP_PKEY_free(p); + } +}; + +struct PkeyCtx { + EVP_PKEY_CTX* p = nullptr; + explicit PkeyCtx(EVP_PKEY_CTX* x = nullptr) : p(x) {} + ~PkeyCtx() { + if (p) EVP_PKEY_CTX_free(p); + } + operator EVP_PKEY_CTX*() const { return p; } +}; + +struct CipherCtx { + EVP_CIPHER_CTX* p = EVP_CIPHER_CTX_new(); + ~CipherCtx() { + if (p) EVP_CIPHER_CTX_free(p); + } + operator EVP_CIPHER_CTX*() const { return p; } +}; + +/* ------------------------------------------------ */ +/* Helpers */ +/* ------------------------------------------------ */ + +static bool generate_ec_key(Pkey& out) { + PkeyCtx ctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr)); + if (!ctx) return false; + + if (EVP_PKEY_keygen_init(ctx) != 1) return false; + + OSSL_PARAM params[] = {OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + (char*)"secp256k1", 0), + OSSL_PARAM_END}; + + return EVP_PKEY_CTX_set_params(ctx, params) == 1 && + EVP_PKEY_keygen(ctx, &out.p) == 1; +} + +static bool import_pubkey(const unsigned char* buf, size_t len, Pkey& out) { + if (len != PUBKEY_LEN) return false; + + PkeyCtx ctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr)); + if (!ctx) return false; + + if (EVP_PKEY_fromdata_init(ctx) != 1) return false; + + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char*)"secp256k1", + 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, (void*)buf, len), + OSSL_PARAM_END}; + + if (EVP_PKEY_fromdata(ctx, &out.p, EVP_PKEY_PUBLIC_KEY, params) != 1) + return false; + + PkeyCtx vctx(EVP_PKEY_CTX_new(out.p, nullptr)); + return vctx && EVP_PKEY_public_check(vctx) == 1; +} + +static bool ecdh(EVP_PKEY* priv, EVP_PKEY* pub, + std::array& out) { + size_t len = out.size(); + PkeyCtx ctx(EVP_PKEY_CTX_new(priv, nullptr)); + return ctx && EVP_PKEY_derive_init(ctx) == 1 && + EVP_PKEY_derive_set_peer(ctx, pub) == 1 && + EVP_PKEY_derive(ctx, out.data(), &len) == 1 && len == out.size(); +} + +static bool hkdf_sha256(const unsigned char* ikm, size_t ikm_len, + const unsigned char* salt, size_t salt_len, + const char* info, std::array& out) { + size_t len = out.size(); + PkeyCtx ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + return ctx && EVP_PKEY_derive_init(ctx) == 1 && + EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) == 1 && + EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, salt_len) == 1 && + EVP_PKEY_CTX_set1_hkdf_key(ctx, ikm, ikm_len) == 1 && + EVP_PKEY_CTX_add1_hkdf_info(ctx, (const unsigned char*)info, + std::strlen(info)) == 1 && + EVP_PKEY_derive(ctx, out.data(), &len) == 1 && len == out.size(); +} + +/* ------------------------------------------------ */ +/* ECIES Encrypt */ +/* ------------------------------------------------ */ + +bool ECIES_Encrypt(EVP_PKEY* recipient_pub, const ByteVector& plaintext, + ByteVector& out) { + if (!recipient_pub || plaintext.empty()) return false; + + Pkey eph; + if (!generate_ec_key(eph)) return false; + + std::array shared; + if (!ecdh(eph.p, recipient_pub, shared)) return false; + + std::array eph_pub{}; + size_t eph_len = eph_pub.size(); + if (EVP_PKEY_get_octet_string_param(eph.p, OSSL_PKEY_PARAM_PUB_KEY, + eph_pub.data(), eph_pub.size(), + &eph_len) != 1 || + eph_len != PUBKEY_LEN) + return false; + + std::array key; + if (!hkdf_sha256(shared.data(), shared.size(), eph_pub.data(), + eph_pub.size(), "ecies-v1-encrypt", key)) + return false; + + std::array nonce; + if (RAND_bytes(nonce.data(), nonce.size()) != 1) return false; + + CipherCtx cctx; + if (!cctx) return false; + + std::vector ciphertext(plaintext.size()); + int len = 0, ct_len = 0; + + EVP_EncryptInit_ex(cctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr); + EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, nullptr); + EVP_EncryptInit_ex(cctx, nullptr, nullptr, key.data(), nonce.data()); + + EVP_EncryptUpdate(cctx, nullptr, &len, &ECIES_VERSION, 1); + EVP_EncryptUpdate(cctx, nullptr, &len, eph_pub.data(), eph_pub.size()); + EVP_EncryptUpdate(cctx, nullptr, &len, nonce.data(), nonce.size()); + + EVP_EncryptUpdate(cctx, ciphertext.data(), &len, plaintext.data(), + plaintext.size()); + ct_len = len; + + if (EVP_EncryptFinal_ex(cctx, ciphertext.data() + ct_len, &len) != 1) + return false; + ct_len += len; + + std::array tag; + EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_GET_TAG, TAG_LEN, tag.data()); + + out.clear(); + out.reserve(1 + PUBKEY_LEN + NONCE_LEN + ct_len + TAG_LEN); + + out.push_back(ECIES_VERSION); + out.insert(out.end(), eph_pub.begin(), eph_pub.end()); + out.insert(out.end(), nonce.begin(), nonce.end()); + out.insert(out.end(), ciphertext.begin(), ciphertext.begin() + ct_len); + out.insert(out.end(), tag.begin(), tag.end()); + + OPENSSL_cleanse(shared.data(), shared.size()); + OPENSSL_cleanse(key.data(), key.size()); + return true; +} + +/* ------------------------------------------------ */ +/* ECIES Decrypt */ +/* ------------------------------------------------ */ + +bool ECIES_Decrypt(EVP_PKEY* recipient_priv, const ByteVector& enc, + ByteVector& out) { + if (!recipient_priv || enc.size() < 1 + PUBKEY_LEN + NONCE_LEN + TAG_LEN) + return false; + + const unsigned char* p = enc.data(); + if (*p++ != ECIES_VERSION) return false; + + const unsigned char* eph_pub = p; + p += PUBKEY_LEN; + const unsigned char* nonce = p; + p += NONCE_LEN; + + size_t ct_len = enc.size() - 1 - PUBKEY_LEN - NONCE_LEN - TAG_LEN; + const unsigned char* ct = p; + const unsigned char* tag = p + ct_len; + + Pkey eph; + if (!import_pubkey(eph_pub, PUBKEY_LEN, eph)) return false; + + std::array shared; + if (!ecdh(recipient_priv, eph.p, shared)) return false; + + std::array key; + if (!hkdf_sha256(shared.data(), shared.size(), eph_pub, PUBKEY_LEN, + "ecies-v1-encrypt", key)) + return false; + + CipherCtx cctx; + if (!cctx) return false; + + out.resize(ct_len); + int len = 0; + + EVP_DecryptInit_ex(cctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr); + EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, nullptr); + EVP_DecryptInit_ex(cctx, nullptr, nullptr, key.data(), nonce); + + EVP_DecryptUpdate(cctx, nullptr, &len, &ECIES_VERSION, 1); + EVP_DecryptUpdate(cctx, nullptr, &len, eph_pub, PUBKEY_LEN); + EVP_DecryptUpdate(cctx, nullptr, &len, nonce, NONCE_LEN); + + EVP_DecryptUpdate(cctx, out.data(), &len, ct, ct_len); + EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_SET_TAG, TAG_LEN, (void*)tag); + + bool ok = EVP_DecryptFinal_ex(cctx, out.data() + len, &len) == 1; + + OPENSSL_cleanse(shared.data(), shared.size()); + OPENSSL_cleanse(key.data(), key.size()); + return ok; +} + +/* ------------------------------------------------ */ +/* High-level wrappers */ +/* ------------------------------------------------ */ + +static void set_error(char* err, size_t len, const char* msg) { + if (err && len) std::snprintf(err, len, "%s", msg); +} + +ByteVector ecies_encrypt(const ecies_ctx_t* ctx, const unsigned char* data, + size_t length, char* error, size_t error_len) { + if (!ctx || !ctx->recipient_pub || !data || length == 0) { + set_error(error, error_len, "Invalid arguments"); + return {}; + } + + ByteVector plaintext(data, data + length); + ByteVector encrypted; + + if (!ECIES_Encrypt(ctx->recipient_pub, plaintext, encrypted)) { + set_error(error, error_len, "ECIES encryption failed"); + return {}; + } + + return encrypted; +} + +ByteVector ecies_decrypt(const ecies_ctx_t* ctx, const ByteVector& cryptex, + char* error, size_t error_len) { + if (!ctx || !ctx->recipient_priv || cryptex.empty()) { + set_error(error, error_len, "Invalid arguments"); + return {}; + } + + ByteVector decrypted; + if (!ECIES_Decrypt(ctx->recipient_priv, cryptex, decrypted)) { + set_error(error, error_len, "ECIES decryption failed"); + return {}; + } + + return decrypted; +} diff --git a/src/ecies/ecies.h b/src/ecies/ecies.h index 18202b9d..ed3986f5 100644 --- a/src/ecies/ecies.h +++ b/src/ecies/ecies.h @@ -1,71 +1,43 @@ -/** - * @file /cryptron/ecies.h - * - * @brief ECIES module functions. - * - * $Author: Ladar Levison $ - * $Website: http://lavabit.com $ - * $Date: 2010/08/06 06:02:03 $ - * $Revision: a51931d0f81f6abe29ca91470931d41a374508a7 $ - * - * I hereby place the attached code in the public domain. - * As such it comes without any warranty regarding its merchantability or - * fitness for a particular purpose. Please use it at your own risk. - * - */ +// Copyright (c) 2026 sats0k +// Distributed under the MIT/X11 software licence, see the accompanying +// file LICENCE or http://opensource.org/license/mit -#ifndef LAVABIT_ECIES_H -#define LAVABIT_ECIES_H +#ifndef ECIES_H +#define ECIES_H -#if (__cplusplus) -extern "C" { -#endif +#pragma once -#include +#include #include -#include - +#include #include -#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) -#include /* for ECDH_KDF_X9_62() */ +#if OPENSSL_VERSION_NUMBER < 0x30500000L +#error "This ECIES implementation requires OpenSSL 3.5+" #endif -typedef struct { - struct { - size_t key; - size_t mac; - size_t body; - } length; -} secure_head_t; +#include +#include -typedef struct { - const EVP_CIPHER *cipher; - const EVP_MD *md; - const EVP_MD *kdf_md; - size_t stored_key_length; - const EC_KEY *user_key; -} ecies_ctx_t; +using ByteVector = std::vector; -typedef unsigned char *secure_t; +struct ecies_ctx_t { + EVP_PKEY* recipient_pub; // for encrypt + EVP_PKEY* recipient_priv; // for decrypt +}; -secure_t *ecies_encrypt(const ecies_ctx_t *ctx, const unsigned char *data, size_t length, char *error); -unsigned char *ecies_decrypt(const ecies_ctx_t *ctx, const secure_t *cryptex, size_t *length, char *error); +/* Low-level ECIES */ +bool ECIES_Encrypt(EVP_PKEY* recipient_pub, const ByteVector& plaintext, + ByteVector& out); -unsigned char *secure_key_data(const secure_t *cryptex); -unsigned char *secure_mac_data(const secure_t *cryptex); -unsigned char *secure_body_data(const secure_t *cryptex); -size_t secure_key_length(const secure_t *cryptex); -size_t secure_mac_length(const secure_t *cryptex); -size_t secure_body_length(const secure_t *cryptex); -size_t secure_data_sum_length(const secure_t *cryptex); -size_t secure_total_length(const secure_t *cryptex); +bool ECIES_Decrypt(EVP_PKEY* recipient_priv, const ByteVector& enc, + ByteVector& out); -secure_t *secure_alloc(size_t key, size_t mac, size_t body); -void secure_free(secure_t *cryptex); +/* High-level wrappers */ +ByteVector ecies_encrypt(const ecies_ctx_t* ctx, const unsigned char* data, + size_t length, char* error, size_t error_len); -#if (__cplusplus) -} -#endif +ByteVector ecies_decrypt(const ecies_ctx_t* ctx, const ByteVector& cryptex, + char* error, size_t error_len); -#endif /* LAVABIT_ECIES_H */ +#endif /* ECIES_H */ diff --git a/src/ecies/kdf.c b/src/ecies/kdf.c deleted file mode 100644 index 789fbb82..00000000 --- a/src/ecies/kdf.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the OpenSSL license (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#include - -#if (OPENSSL_VERSION_NUMBER < 0x10002000L) - -/* originally from crypto/ec/ecdh_kdf.c */ - -#include -#include - -/* Key derivation function from X9.62/SECG */ -/* Way more than we will ever need */ -#define ECDH_KDF_MAX (1 << 30) - -int ECDH_KDF_X9_62(unsigned char *out, size_t outlen, const unsigned char *Z, - size_t Zlen, const unsigned char *sinfo, size_t sinfolen, const EVP_MD *md) { - int rv = 0; - unsigned int i; - size_t mdlen; - unsigned char ctr[4]; - EVP_MD_CTX mctx; - - if((sinfolen > ECDH_KDF_MAX) || (outlen > ECDH_KDF_MAX) || - (Zlen > ECDH_KDF_MAX)) return(0); - - mdlen = EVP_MD_size(md); - - EVP_MD_CTX_init(&mctx); - - for(i = 1; ; i++) { - unsigned char mtmp[EVP_MAX_MD_SIZE]; - - if(!EVP_DigestInit_ex(&mctx, md, NULL)) - goto err; - - ctr[3] = i & 0xFF; - ctr[2] = (i >> 8) & 0xFF; - ctr[1] = (i >> 16) & 0xFF; - ctr[0] = (i >> 24) & 0xFF; - - if(!EVP_DigestUpdate(&mctx, Z, Zlen)) - goto err; - - if(!EVP_DigestUpdate(&mctx, ctr, sizeof(ctr))) - goto err; - - if(!EVP_DigestUpdate(&mctx, sinfo, sinfolen)) - goto err; - - if(outlen >= mdlen) { - if(!EVP_DigestFinal(&mctx, out, NULL)) - goto err; - - outlen -= mdlen; - if(!outlen) break; - out += mdlen; - } else { - if(!EVP_DigestFinal(&mctx, mtmp, NULL)) - goto err; - - memcpy(out, mtmp, outlen); - OPENSSL_cleanse(mtmp, mdlen); - break; - } - } - rv = 1; - -err: - EVP_MD_CTX_cleanup(&mctx); - return(rv); -} - -#endif diff --git a/src/ecies/secure.c b/src/ecies/secure.c deleted file mode 100644 index 965e72a0..00000000 --- a/src/ecies/secure.c +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file /cryptron/secure.c - * - * @brief Functions for handling the secure data type. - * - * $Author: Ladar Levison $ - * $Website: http://lavabit.com $ - * $Date: 2010/08/05 11:43:50 $ - * $Revision: c363dfa193830feb5d014a7c6f0abf2d1365f668 $ - * - * I hereby place the attached code in the public domain. - * As such it comes without any warranty regarding its merchantability or - * fitness for a particular purpose. Please use it at your own risk. - * - */ - -#include "ecies.h" - -#define HEADSIZE (sizeof(secure_head_t)) - -size_t secure_key_length(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return(head->length.key); -} - -size_t secure_mac_length(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return(head->length.mac); -} - -size_t secure_body_length(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return(head->length.body); -} - -size_t secure_data_sum_length(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return(head->length.key + head->length.mac + head->length.body); -} - -size_t secure_total_length(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return(HEADSIZE + (head->length.key + head->length.mac + head->length.body)); -} - -unsigned char *secure_key_data(const secure_t *cryptex) { - return((unsigned char *) cryptex + HEADSIZE); -} - -unsigned char *secure_mac_data(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return((unsigned char *) cryptex + (HEADSIZE + head->length.key + head->length.body)); -} - -unsigned char *secure_body_data(const secure_t *cryptex) { - secure_head_t *head = (secure_head_t *) cryptex; - return((unsigned char *) cryptex + (HEADSIZE + head->length.key)); -} - -secure_t *secure_alloc(size_t key, size_t mac, size_t body) { - secure_t *cryptex = (secure_t *) malloc(HEADSIZE + key + mac + body); - secure_head_t *head = (secure_head_t *) cryptex; - head->length.key = key; - head->length.mac = mac; - head->length.body = body; - return(cryptex); -} - -void secure_free(secure_t *cryptex) { - free(cryptex); - return; -} diff --git a/src/ecies/secure.cpp b/src/ecies/secure.cpp new file mode 100644 index 00000000..0252c097 --- /dev/null +++ b/src/ecies/secure.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2026 sats0k +// Distributed under the MIT/X11 software licence, see the accompanying +// file LICENCE or http://opensource.org/license/mit + +#include "ecies.h" + +constexpr size_t ECIES_PUBKEY_LEN = 65; +constexpr size_t ECIES_NONCE_LEN = 12; +constexpr size_t ECIES_TAG_LEN = 16; + +struct ecies_view { + const unsigned char* eph_pub; + const unsigned char* nonce; + const unsigned char* ciphertext; + const unsigned char* tag; + size_t ciphertext_len; +}; + +inline bool ecies_parse(const ByteVector& v, ecies_view& out) { + if (v.size() < ECIES_PUBKEY_LEN + ECIES_NONCE_LEN + ECIES_TAG_LEN) + return false; + + const unsigned char* p = v.data(); + + out.eph_pub = p; + p += ECIES_PUBKEY_LEN; + + out.nonce = p; + p += ECIES_NONCE_LEN; + + out.ciphertext_len = + v.size() - ECIES_PUBKEY_LEN - ECIES_NONCE_LEN - ECIES_TAG_LEN; + + out.ciphertext = p; + out.tag = p + out.ciphertext_len; + + return true; +} diff --git a/src/key.cpp b/src/key.cpp index cd2eedf8..7940f1d3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,486 +1,696 @@ // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2026 sats0k // Distributed under the MIT/X11 software licence, see the accompanying // file LICENCE or http://opensource.org/license/mit -#include - -#include -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) -#include -#endif - +// Project #include "key.h" - #include "ecies/ecies.h" -// Generate a private key from just the secret parameter -int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { - int ok = 0; - BN_CTX *ctx = NULL; - EC_POINT *pub_key = NULL; - - if(!eckey) return(0); - - const EC_GROUP *group = EC_KEY_get0_group(eckey); +// C / C++ +#include +#include +#include +#include + +// OpenSSL (core + 3.x APIs) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libsecp256k1 +#include +#include + +/* ---------- Global secp256k1 context ---------- */ +static secp256k1_context* g_secp256k1_verify_ctx = [] { + auto* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + if (!ctx) + throw std::runtime_error("Failed to create secp256k1 verify context"); + return ctx; +}(); + +static thread_local secp256k1_context* g_secp256k1_sign_ctx = [] { + auto* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + if (!ctx) + throw std::runtime_error("Failed to create secp256k1 sign context"); + + unsigned char seed[32]; + if (RAND_bytes(seed, sizeof(seed)) != 1) + throw std::runtime_error("RAND_bytes failed"); + + if (!secp256k1_context_randomize(ctx, seed)) + throw std::runtime_error("Failed to randomize secp256k1 sign context"); + + return ctx; +}(); + +/* ---------- Forward declarations ---------- */ + +static CPubKey SerializePubKey(const secp256k1_pubkey& pub) { + unsigned char out[33]; + size_t len = 33; + + secp256k1_ec_pubkey_serialize(g_secp256k1_sign_ctx, out, &len, &pub, + SECP256K1_EC_COMPRESSED); + + return CPubKey(std::vector(out, out + len)); +} - if((ctx = BN_CTX_new()) == NULL) - goto err; +/* ---------- Compressed -> uncompressed helper (for OpenSSL) ---------- */ +static bool DecompressPubKey(const unsigned char* comp, unsigned char out[65]) { + secp256k1_pubkey pub; - pub_key = EC_POINT_new(group); + if (!secp256k1_ec_pubkey_parse(g_secp256k1_verify_ctx, &pub, comp, 33)) + return false; - if(!pub_key) - goto err; + size_t len = 65; + secp256k1_ec_pubkey_serialize(g_secp256k1_verify_ctx, out, &len, &pub, + SECP256K1_EC_UNCOMPRESSED); - if(!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) - goto err; + return true; +} - EC_KEY_set_private_key(eckey,priv_key); - EC_KEY_set_public_key(eckey,pub_key); +/* ---------- Build EVP_PKEY from raw secret ---------- */ +static EVP_PKEY* MakePKeyFromSecret(const unsigned char* secret, + size_t secret_len, + const unsigned char* pubkey, + size_t pubkey_len) { + if (!secret || secret_len != 32) return nullptr; + + unsigned char uncompressed[65]; + + if (pubkey_len == 33) { + if (!DecompressPubKey(pubkey, uncompressed)) return nullptr; + pubkey = uncompressed; + pubkey_len = 65; + } else if (pubkey_len == 65) { + if (pubkey[0] != 0x04) return nullptr; + } else { + return nullptr; + } - ok = 1; + static OSSL_LIB_CTX* libctx = [] { + OSSL_LIB_CTX* c = OSSL_LIB_CTX_new(); + if (!OSSL_PROVIDER_load(c, "default")) + throw std::runtime_error("default provider load failed"); + return c; + }(); -err: + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", nullptr); + if (!ctx) return nullptr; - if(pub_key) - EC_POINT_free(pub_key); - if(ctx != NULL) - BN_CTX_free(ctx); + EVP_PKEY* pkey = nullptr; - return(ok); -} + if (EVP_PKEY_fromdata_init(ctx) <= 0) { + ERR_print_errors_fp(stderr); + EVP_PKEY_CTX_free(ctx); + return nullptr; + } -// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields -// recid selects which key is recovered -// if check is non-zero, additional checks are performed -int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const uchar *msg, - int msglen, int recid, int check) { - - if(!eckey) return(0); - -#if (OPENSSL_VERSION_NUMBER > 0x10100000L) - const BIGNUM *sig_r, *sig_s; - ECDSA_SIG_get0(ecsig, &sig_r, &sig_s); -#endif - - int ret = 0; - BN_CTX *ctx = NULL; - - BIGNUM *x = NULL; - BIGNUM *e = NULL; - BIGNUM *order = NULL; - BIGNUM *sor = NULL; - BIGNUM *eor = NULL; - BIGNUM *field = NULL; - EC_POINT *R = NULL; - EC_POINT *O = NULL; - EC_POINT *Q = NULL; - BIGNUM *rr = NULL; - BIGNUM *zero = NULL; - int n = 0; - int i = recid / 2; - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - if((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } - BN_CTX_start(ctx); - order = BN_CTX_get(ctx); - if(!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } - x = BN_CTX_get(ctx); - if(!BN_copy(x, order)) { ret = -1; goto err; } - if(!BN_mul_word(x, i)) { ret = -1; goto err; } -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - if(!BN_add(x, x, ecsig->r)) { ret = -1; goto err; } -#else - if(!BN_add(x, x, sig_r)) { ret = -1; goto err; } -#endif - field = BN_CTX_get(ctx); - if(!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret = -2; goto err; } - if(BN_cmp(x, field) >= 0) { ret = 0; goto err; } - if((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if(!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret = 0; goto err; } - if(check) { - if((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if(!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret = -2; goto err; } - if(!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + BIGNUM* bn = BN_bin2bn(secret, 32, nullptr); + if (!bn) { + EVP_PKEY_CTX_free(ctx); + return nullptr; } - if((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - n = EC_GROUP_get_degree(group); - e = BN_CTX_get(ctx); - if(!BN_bin2bn(msg, msglen, e)) { ret = -1; goto err; } - if((8 * msglen) > n) BN_rshift(e, e, 8 - (n & 7)); - zero = BN_CTX_get(ctx); - if(!BN_zero(zero)) { ret = -1; goto err; } - if(!BN_mod_sub(e, zero, e, order, ctx)) { ret = -1; goto err; } - rr = BN_CTX_get(ctx); -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - if(!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret = -1; goto err; } -#else - if(!BN_mod_inverse(rr, sig_r, order, ctx)) { ret = -1; goto err; } -#endif - sor = BN_CTX_get(ctx); -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - if(!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret = -1; goto err; } -#else - if(!BN_mod_mul(sor, sig_s, rr, order, ctx)) { ret = -1; goto err; } -#endif - eor = BN_CTX_get(ctx); - if(!BN_mod_mul(eor, e, rr, order, ctx)) { ret = -1; goto err; } - if(!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret = -2; goto err; } - if(!EC_KEY_set_public_key(eckey, Q)) { ret = -2; goto err; } - - ret = 1; -err: - if(ctx) { - BN_CTX_end(ctx); - BN_CTX_free(ctx); + unsigned char bn_buf[32]; + BN_bn2binpad(bn, bn_buf, sizeof(bn_buf)); + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char*)"secp256k1", + 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, bn_buf, sizeof(bn_buf)), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, (void*)pubkey, 65), + OSSL_PARAM_END}; + + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) { + ERR_print_errors_fp(stderr); + BN_clear_free(bn); + EVP_PKEY_CTX_free(ctx); + return nullptr; } - if(R != NULL) EC_POINT_free(R); - if(O != NULL) EC_POINT_free(O); - if(Q != NULL) EC_POINT_free(Q); - return(ret); -} -void CKey::SetCompressedPubKey(bool fCompressed) { - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - fCompressedPubKey = true; + BN_clear_free(bn); + EVP_PKEY_CTX_free(ctx); + return pkey; } -void CKey::Reset() { - fCompressedPubKey = false; - if(pkey) - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(!pkey) - throw(key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed")); - fSet = false; -} +/* ---------- Recover pubkey from sig ---------- */ +CPubKey RecoverPubKey(const uint256& hash, const unsigned char sig64[64], + int recid, bool compressed) { + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_pubkey pub; -CKey::CKey() { - pkey = NULL; - Reset(); -} + if (!secp256k1_ecdsa_recoverable_signature_parse_compact( + g_secp256k1_verify_ctx, &rsig, sig64, recid)) + return CPubKey(); -CKey::CKey(const CKey &b) { - pkey = EC_KEY_dup(b.pkey); - if(!pkey) - throw(key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed")); - fSet = b.fSet; -} + if (!secp256k1_ecdsa_recover(g_secp256k1_verify_ctx, &pub, &rsig, + reinterpret_cast(&hash))) + return CPubKey(); -CKey &CKey::operator=(const CKey &b) { - if(!EC_KEY_copy(pkey, b.pkey)) - throw(key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed")); - fSet = b.fSet; - return(*this); + return SerializePubKey(pub); } -CKey::~CKey() { - EC_KEY_free(pkey); -} +/* ---------- CKey methods ---------- */ -bool CKey::IsNull() const { - return(!fSet); -} +void CKey::SetCompressedPubKey() { fCompressedPubKey = true; } -bool CKey::IsCompressed() const { - return(fCompressedPubKey); -} +bool CKey::IsNull() const { return !fSet; } +bool CKey::IsCompressed() const { return fCompressedPubKey; } void CKey::MakeNewKey(bool fCompressed) { - if(!EC_KEY_generate_key(pkey)) - throw(key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed")); - if(fCompressed) - SetCompressedPubKey(); + Reset(); + + CSecret secret(32); + do { + if (RAND_bytes(secret.data(), 32) != 1) + throw key_error("RAND_bytes failed"); + } while (!secp256k1_ec_seckey_verify(g_secp256k1_sign_ctx, secret.data())); + + secp256k1_pubkey pub; + if (!secp256k1_ec_pubkey_create(g_secp256k1_sign_ctx, &pub, secret.data())) + throw key_error("Failed to create public key"); + + CPubKey pubkey = SerializePubKey(pub); + EVP_PKEY* tmp = MakePKeyFromSecret( + secret.data(), secret.size(), pubkey.Raw().data(), pubkey.Raw().size()); + if (!tmp) throw key_error("EVP_PKEY creation failed"); + + pkey = tmp; + vchSecret = secret; + vchPubKey = pubkey; + fCompressedPubKey = fCompressed; fSet = true; } -bool CKey::SetPrivKey(const CPrivKey &vchPrivKey) { - const uchar *pbegin = &vchPrivKey[0]; - if(d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) { - // In testing, d2i_ECPrivateKey can return true - // but fill in pkey with a key that fails - // EC_KEY_check_key, so: - if(EC_KEY_check_key(pkey)) { - fSet = true; - return true; - } - } - // If vchPrivKey data is bad d2i_ECPrivateKey() can - // leave pkey in a state where calling EC_KEY_free() - // crashes. To avoid that, set pkey to NULL and - // leak the memory (a leak is better than a crash) - pkey = NULL; +bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) { Reset(); - return(false); -} -bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) { - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(!pkey) - throw(key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed")); - if(vchSecret.size() != 32) - throw(key_error("CKey::SetSecret() : secret must be 32 bytes")); - BIGNUM *bn = BN_bin2bn(&vchSecret[0], 32, BN_new()); - if(!bn) - throw(key_error("CKey::SetSecret() : BN_bin2bn failed")); - if(!EC_KEY_regenerate_key(pkey, bn)) { - BN_clear_free(bn); - throw(key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed")); + if (vchPrivKey.size() == 32) { + CSecret secret(vchPrivKey.begin(), vchPrivKey.end()); + return SetSecret(secret, false); } + + const unsigned char* p = vchPrivKey.data(); + EVP_PKEY* tmp = d2i_AutoPrivateKey(nullptr, &p, vchPrivKey.size()); + if (!tmp) return false; + + EVP_PKEY_CTX* vctx = EVP_PKEY_CTX_new(tmp, nullptr); + bool ok = vctx && EVP_PKEY_check(vctx) == 1; + EVP_PKEY_CTX_free(vctx); + if (!ok) { + EVP_PKEY_free(tmp); + return false; + } + + BIGNUM* bn = nullptr; + if (!EVP_PKEY_get_bn_param(tmp, OSSL_PKEY_PARAM_PRIV_KEY, &bn)) { + EVP_PKEY_free(tmp); + return false; + } + + CSecret secret(32, 0); + BN_bn2binpad(bn, secret.data(), 32); BN_clear_free(bn); + + secp256k1_pubkey pub; + if (!secp256k1_ec_pubkey_create(g_secp256k1_sign_ctx, &pub, + secret.data())) { + EVP_PKEY_free(tmp); + return false; + } + + CPubKey pubkey = SerializePubKey(pub); + + pkey = tmp; + vchSecret = secret; + vchPubKey = pubkey; + fCompressedPubKey = true; + fSet = true; + return true; +} + +bool CKey::SetSecret(const CSecret& secret, bool fCompressed) { + Reset(); + + if (secret.size() != 32) return false; + if (!secp256k1_ec_seckey_verify(g_secp256k1_sign_ctx, secret.data())) + return false; + + secp256k1_pubkey pub; + if (!secp256k1_ec_pubkey_create(g_secp256k1_sign_ctx, &pub, secret.data())) + return false; + + CPubKey pubkey = SerializePubKey(pub); + EVP_PKEY* tmp = MakePKeyFromSecret( + secret.data(), secret.size(), pubkey.Raw().data(), pubkey.Raw().size()); + if (!tmp) return false; + + pkey = tmp; + vchSecret = secret; + vchPubKey = pubkey; + fCompressedPubKey = fCompressed; fSet = true; - if(fCompressed || fCompressedPubKey) - SetCompressedPubKey(); - return(true); + return true; } -CSecret CKey::GetSecret(bool &fCompressed) const { - CSecret vchRet; - vchRet.resize(32); - const BIGNUM *bn = EC_KEY_get0_private_key(pkey); - int nBytes = BN_num_bytes(bn); - if(!bn) - throw(key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed")); - int n = BN_bn2bin(bn, &vchRet[32 - nBytes]); - if(n != nBytes) - throw(key_error("CKey::GetSecret(): BN_bn2bin failed")); +CSecret CKey::GetSecret(bool& fCompressed) const { + if (!fSet) throw key_error("CKey::GetSecret(): key not set"); fCompressed = fCompressedPubKey; - return(vchRet); + return vchSecret; } CPrivKey CKey::GetPrivKey() const { - int nSize = i2d_ECPrivateKey(pkey, NULL); - if(!nSize) - throw(key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed")); - CPrivKey vchPrivKey(nSize, 0); - uchar *pbegin = &vchPrivKey[0]; - if(i2d_ECPrivateKey(pkey, &pbegin) != nSize) - throw(key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size")); - return(vchPrivKey); + if (!fSet) throw key_error("CKey::GetPrivKey(): key not set"); + return CPrivKey(vchSecret.begin(), vchSecret.end()); } -bool CKey::SetPubKey(const CPubKey &vchPubKey) { - const uchar *pbegin = &vchPubKey.vchPubKey[0]; - if(o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) { - fSet = true; - if(vchPubKey.vchPubKey.size() == 33) - SetCompressedPubKey(); - return(true); - } - pkey = NULL; +bool CKey::SetPubKey(const CPubKey& vchPubKeyIn) { Reset(); - return(false); + + const std::vector& pubkey = vchPubKeyIn.vchPubKey; + const unsigned char* pub = pubkey.data(); + size_t pub_len = pubkey.size(); + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr); + if (!ctx) return false; + + EVP_PKEY* tmp = nullptr; + + if (EVP_PKEY_fromdata_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + return false; + } + + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char*)"secp256k1", + 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, (void*)pub, pub_len), + OSSL_PARAM_END}; + + if (EVP_PKEY_fromdata(ctx, &tmp, EVP_PKEY_PUBLIC_KEY, params) <= 0) { + EVP_PKEY_CTX_free(ctx); + return false; + } + + EVP_PKEY_CTX_free(ctx); + + EVP_PKEY_CTX* vctx = EVP_PKEY_CTX_new(tmp, nullptr); + if (!vctx || EVP_PKEY_public_check(vctx) != 1) { + EVP_PKEY_CTX_free(vctx); + EVP_PKEY_free(tmp); + return false; + } + EVP_PKEY_CTX_free(vctx); + + pkey = tmp; + fSet = true; + vchPubKey = vchPubKeyIn; + fCompressedPubKey = (pub_len == 33 && (pub[0] == 0x02 || pub[0] == 0x03)); + + return true; } CPubKey CKey::GetPubKey() const { - int nSize = i2o_ECPublicKey(pkey, NULL); - if(!nSize) - throw(key_error("CKey::GetPubKey() : i2o_ECPublicKey failed")); - std::vector vchPubKey(nSize, 0); - uchar *pbegin = &vchPubKey[0]; - if(i2o_ECPublicKey(pkey, &pbegin) != nSize) - throw(key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size")); - return(CPubKey(vchPubKey)); + if (!fCompressedPubKey) { + // legacy key → force upgrade + const_cast(this)->fCompressedPubKey = true; + } + return vchPubKey; } -bool CKey::Sign(uint256 hash, std::vector &vchSig) { - uint nSize = ECDSA_size(pkey); - vchSig.resize(nSize); // Make sure it is big enough - if(!ECDSA_sign(0, (uchar *) &hash, sizeof(hash), &vchSig[0], &nSize, pkey)) { - vchSig.clear(); - return(false); - } - vchSig.resize(nSize); // Shrink to fit actual size - return(true); +/* ---------- Signing ---------- */ + +bool CKey::Sign(uint256 hash, std::vector& sig) const { + if (!fSet) return false; + + secp256k1_ecdsa_signature signature; + if (!secp256k1_ecdsa_sign(g_secp256k1_sign_ctx, &signature, hash.begin(), + vchSecret.data(), nullptr, nullptr)) + return false; + + secp256k1_ecdsa_signature sig_norm; + secp256k1_ecdsa_signature_normalize(g_secp256k1_sign_ctx, &sig_norm, + &signature); + signature = sig_norm; + + unsigned char der[72]; + size_t derlen = sizeof(der); + secp256k1_ecdsa_signature_serialize_der(g_secp256k1_sign_ctx, der, &derlen, + &signature); + sig.assign(der, der + derlen); + return true; } -// create a compact signature (65 bytes), which allows reconstructing the used public key -// The format is one header byte, followed by two times 32 bytes for the serialized r and s values. -// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, -// 0x1D = second key with even y, 0x1E = second key with odd y -bool CKey::SignCompact(uint256 hash, std::vector &vchSig) { - bool fOk = false; - ECDSA_SIG *sig = ECDSA_do_sign((uchar *) &hash, sizeof(hash), pkey); - if(!sig) return(false); +bool CKey::SignCompact(const uint256& hash, + std::vector& vchSig) const { + unsigned char privkey[32]; + std::memcpy(privkey, vchSecret.data(), 32); vchSig.clear(); - vchSig.resize(65, 0); -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); -#else - const BIGNUM *sig_r, *sig_s; - ECDSA_SIG_get0(sig, &sig_r, &sig_s); - - int nBitsR = BN_num_bits(sig_r); - int nBitsS = BN_num_bits(sig_s); -#endif - - int i; - if((nBitsR <= 256) && (nBitsS <= 256)) { - int nRecId = -1; - for(i = 0; i < 4; i++) { - CKey keyRec; - keyRec.fSet = true; - if(fCompressedPubKey) - keyRec.SetCompressedPubKey(); - if(ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (uchar *) &hash, sizeof(hash), i, 1) == 1) { - if(keyRec.GetPubKey() == this->GetPubKey()) { - nRecId = i; - break; - } - } - } - - if(nRecId == -1) - throw(key_error("CKey::SignCompact() : unable to construct recoverable key")); - - vchSig[0] = nRecId + 27 + (fCompressedPubKey ? 4 : 0); -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - BN_bn2bin(sig->r, &vchSig[33 - (nBitsR + 7) / 8]); - BN_bn2bin(sig->s, &vchSig[65 - (nBitsS + 7) / 8]); -#else - BN_bn2bin(sig_r, &vchSig[33 - (nBitsR + 7) / 8]); - BN_bn2bin(sig_s, &vchSig[65 - (nBitsS + 7) / 8]); -#endif - fOk = true; - } - ECDSA_SIG_free(sig); - return(fOk); -} + vchSig.resize(65); -// reconstruct public key from a compact signature -// This is only slightly more CPU intensive than just verifying it. -// If this function succeeds, the recovered public key is guaranteed to be valid -// (the signature is a valid signature of the given data for that key) -bool CKey::SetCompactSignature(uint256 hash, const std::vector &vchSig) { - if(vchSig.size() != 65) return(false); - int nV = vchSig[0]; - if((nV < 27) || (nV >= 35)) return(false); - ECDSA_SIG *sig = ECDSA_SIG_new(); - if(!sig) return(false); - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - BN_bin2bn(&vchSig[1], 32, sig->r); - BN_bin2bn(&vchSig[33], 32, sig->s); -#else - /* sig_r and sig_s are deallocated by ECDSA_SIG_free(sig); */ - BIGNUM *sig_r = BN_bin2bn(&vchSig[1], 32, BN_new()); - BIGNUM *sig_s = BN_bin2bn(&vchSig[33], 32, BN_new()); - if(!sig_r || !sig_s) return(false); - /* Copy and transfer ownership */ - ECDSA_SIG_set0(sig, sig_r, sig_s); -#endif - - EC_KEY_free(pkey); - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(nV >= 31) { - SetCompressedPubKey(); - nV -= 4; - } - if(ECDSA_SIG_recover_key_GFp(pkey, sig, (uchar *) &hash, sizeof(hash), nV - 27, 0) == 1) { - fSet = true; - ECDSA_SIG_free(sig); - return(true); + unsigned char hashData[32]; + std::memcpy(hashData, hash.begin(), 32); + secp256k1_ecdsa_recoverable_signature sig; + + int signResult = secp256k1_ecdsa_sign_recoverable( + g_secp256k1_sign_ctx, &sig, hashData, privkey, + secp256k1_nonce_function_rfc6979, nullptr); + + if (signResult != 1) { + printf("Signing failed with code: %d\n", signResult); + return false; } - return(false); -} -bool CKey::Verify(uint256 hash, const std::vector &vchSig) { - // -1 = error, 0 = bad sig, 1 = good - if(ECDSA_verify(0, (uchar *) &hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) - return(false); + int recid = 0; + unsigned char sig64[64]; + + secp256k1_ecdsa_recoverable_signature_serialize_compact( + g_secp256k1_sign_ctx, sig64, &recid, &sig); + + vchSig[0] = 27 + recid + (fCompressedPubKey ? 4 : 0); + std::memcpy(&vchSig[1], sig64, 64); - return(true); + return true; } -bool CKey::VerifyCompact(uint256 hash, const std::vector &vchSig) { - CKey key; - if(!key.SetCompactSignature(hash, vchSig)) - return(false); - if(GetPubKey() != key.GetPubKey()) - return(false); +/* ---------- Verification ---------- */ + +bool CKey::SetCompactSignature(const uint256& hash, + const std::vector& vchSig) { + if (vchSig.size() != 65) return false; + + int header = vchSig[0]; + if (header < 27 || header > 34) return false; + + int recid = (header - 27) & 3; + + secp256k1_ecdsa_recoverable_signature sig; + if (!secp256k1_ecdsa_recoverable_signature_parse_compact( + g_secp256k1_verify_ctx, &sig, &vchSig[1], recid)) + return false; + + unsigned char hashData[32]; + std::memcpy(hashData, hash.begin(), 32); + + secp256k1_pubkey pubkey; + if (!secp256k1_ecdsa_recover(g_secp256k1_verify_ctx, &pubkey, &sig, + hashData)) + return false; - return(true); + unsigned char pubkey_out[33]; + size_t pubkey_out_len = 33; + secp256k1_ec_pubkey_serialize(g_secp256k1_verify_ctx, pubkey_out, + &pubkey_out_len, &pubkey, + SECP256K1_EC_COMPRESSED); + std::vector vchPubKey(pubkey_out, + pubkey_out + pubkey_out_len); + SetPubKey(CPubKey(vchPubKey)); + + fCompressedPubKey = true; + fSet = true; + return true; } -bool CKey::IsValid() { - if(!fSet) return(false); +bool CKey::Verify(uint256 hash, const std::vector& sig) const { + secp256k1_pubkey pub; + std::vector pk = vchPubKey.Raw(); + if (!secp256k1_ec_pubkey_parse(g_secp256k1_verify_ctx, &pub, pk.data(), + pk.size())) + return false; + + secp256k1_ecdsa_signature signature; + if (!secp256k1_ecdsa_signature_parse_der(g_secp256k1_verify_ctx, &signature, + sig.data(), sig.size())) + return false; + + secp256k1_ecdsa_signature sig_norm; + secp256k1_ecdsa_signature_normalize(g_secp256k1_verify_ctx, &sig_norm, + &signature); + signature = sig_norm; + + return secp256k1_ecdsa_verify(g_secp256k1_verify_ctx, &signature, + hash.begin(), &pub); +} - if(!EC_KEY_check_key(pkey)) return(false); +bool CKey::VerifyCompact(const uint256& hash, + const std::vector& vchSig) const { + CKey recovered; + if (!recovered.SetCompactSignature(hash, vchSig)) return false; + + const auto& a = recovered.GetPubKey().Raw(); + const auto& b = GetPubKey().Raw(); + return a.size() == b.size() && + CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0; +} + +bool CKey::IsValid() const { + if (!fSet || !pkey) return false; bool fCompr; CSecret secret = GetSecret(fCompr); CKey key2; - key2.SetSecret(secret, fCompr); + if (!key2.SetSecret(secret, fCompr)) return false; + return GetPubKey() == key2.GetPubKey(); } -void CPubKey::EncryptData(const std::vector &plaindata, std::vector &encdata) { - CKey key; +/* ---------- EncryptData DecryptData ---------- */ + +static constexpr size_t NONCE_LEN = 12; +static constexpr size_t TAG_LEN = 16; +static constexpr size_t PUBKEY_LEN = 33; + +EVP_PKEY* CPubKey::GetEVPPubKey() const { + if (!IsValid()) return nullptr; + + EVP_PKEY* tmp = nullptr; + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr); + if (!ctx) return nullptr; + + EVP_PKEY_fromdata_init(ctx); + + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char*)"secp256k1", + 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + (void*)vchPubKey.data(), vchPubKey.size()), + OSSL_PARAM_END}; + + if (EVP_PKEY_fromdata(ctx, &tmp, EVP_PKEY_PUBLIC_KEY, params) <= 0) { + EVP_PKEY_CTX_free(ctx); + return nullptr; + } + + EVP_PKEY_CTX_free(ctx); + return tmp; +} + +EVP_PKEY* CKey::GetEVPPrivKey() const { + if (!pkey) return nullptr; + EVP_PKEY_up_ref(pkey); + return pkey; +} + +static bool ECDH_Derive(EVP_PKEY* privkey, EVP_PKEY* pubkey, + unsigned char out[32]) { + if (!privkey || !pubkey) return false; + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(privkey, nullptr); + if (!ctx) return false; + + unsigned char raw[64]; + size_t rawlen = sizeof(raw); + + bool ok = EVP_PKEY_derive_init(ctx) == 1 && + EVP_PKEY_derive_set_peer(ctx, pubkey) == 1 && + EVP_PKEY_derive(ctx, raw, &rawlen) == 1; + + if (ok) { + SHA256(raw, rawlen, out); + } + + EVP_PKEY_CTX_free(ctx); + return ok; +} + +static bool HKDF_SHA256(const unsigned char* secret, size_t secret_len, + unsigned char* out, size_t out_len) { + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr); + if (!ctx) return false; + + if (EVP_PKEY_derive_init(ctx) != 1) goto err; + if (EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) != 1) goto err; + + /* No salt is allowed — DO NOT test return value */ + EVP_PKEY_CTX_set1_hkdf_salt(ctx, nullptr, 0); + + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, secret, secret_len) != 1) goto err; + static const unsigned char info[] = "secp256k1-ecies-aes256gcm-v1"; - key.SetPubKey(*this); - key.EncryptData(plaindata, encdata); + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, info, sizeof(info) - 1) != 1) goto err; + + if (EVP_PKEY_derive(ctx, out, &out_len) != 1) goto err; + + EVP_PKEY_CTX_free(ctx); + return true; + +err: + EVP_PKEY_CTX_free(ctx); + return false; } -void CKey::EncryptData(const std::vector &plaindata, std::vector &encdata) { - ecies_ctx_t *ctx = new ecies_ctx_t; - char error[256] = "Unknown error"; - secure_t *cryptex; +void CPubKey::EncryptData(const std::vector& plaintext, + std::vector& out) { + if (plaintext.empty()) throw key_error("Empty plaintext"); - ctx->cipher = EVP_aes_128_cbc(); - ctx->md = EVP_ripemd160(); - ctx->kdf_md = EVP_ripemd160(); - ctx->stored_key_length = 33; - ctx->user_key = pkey; + // 1. Ephemeral key + CKey eph; + eph.MakeNewKey(true); - if(!EC_KEY_get0_public_key(ctx->user_key)) - throw(key_error("Invalid public key")); + unsigned char shared[32]; - cryptex = ecies_encrypt(ctx, (uchar *) &plaindata[0], plaindata.size(), error); + EVP_PKEY* priv = eph.GetEVPPrivKey(); + EVP_PKEY* pub = this->GetEVPPubKey(); - if(!cryptex) { - delete ctx; - throw(key_error(std::string("Error in encryption: ") + error)); + if (!ECDH_Derive(priv, pub, shared)) { + EVP_PKEY_free(priv); + EVP_PKEY_free(pub); + throw key_error("ECDH failed"); } - encdata.resize(secure_data_sum_length(cryptex)); - memcpy(&encdata[0], secure_key_data(cryptex), encdata.size()); - secure_free(cryptex); - delete ctx; + EVP_PKEY_free(priv); + EVP_PKEY_free(pub); + + // 2. Derive AEAD key + unsigned char aead_key[32]; + if (!HKDF_SHA256(shared, sizeof(shared), aead_key, sizeof(aead_key))) + throw key_error("HKDF failed"); + + // 3. Nonce + unsigned char nonce[NONCE_LEN]; + if (RAND_bytes(nonce, sizeof(nonce)) != 1) + throw key_error("RAND_bytes failed"); + + // Ephemeral pubkey (for AAD and serialization) + const CPubKey& eph_pub = eph.GetPubKey(); + const std::vector& eph_raw = eph_pub.Raw(); + + // 4. Encrypt + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if (!ctx) throw key_error("Cipher ctx alloc failed"); + + std::vector ciphertext(plaintext.size()); + int len = 0; + + EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, nullptr); + EVP_EncryptInit_ex(ctx, nullptr, nullptr, aead_key, nonce); + + static const unsigned char aad[] = "AES-256-GCM|secp256k1|HKDF-SHA256|v1"; + + EVP_EncryptUpdate(ctx, nullptr, &len, aad, sizeof(aad) - 1); + EVP_EncryptUpdate(ctx, nullptr, &len, eph_raw.data(), eph_raw.size()); + + EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext.data(), + plaintext.size()); + int ct_len = len; + + EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len); + ct_len += len; + + unsigned char tag[TAG_LEN]; + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_LEN, tag); + EVP_CIPHER_CTX_free(ctx); + + // 5. Serialize output + out.clear(); + out.reserve(PUBKEY_LEN + NONCE_LEN + ct_len + TAG_LEN); + + out.insert(out.end(), eph_raw.begin(), eph_raw.end()); + out.insert(out.end(), nonce, nonce + NONCE_LEN); + out.insert(out.end(), ciphertext.begin(), ciphertext.begin() + ct_len); + out.insert(out.end(), tag, tag + TAG_LEN); + + OPENSSL_cleanse(shared, sizeof(shared)); + OPENSSL_cleanse(aead_key, sizeof(aead_key)); } -void CKey::DecryptData(const std::vector &encdata, std::vector &plaindata) { - ecies_ctx_t *ctx = new ecies_ctx_t; - char error[256] = "Unknown error"; - secure_t *cryptex; - uchar *decrypted; - size_t length; +void CKey::DecryptData(const std::vector& enc, + std::vector& out) { + if (enc.size() < PUBKEY_LEN + NONCE_LEN + TAG_LEN) + throw key_error("Ciphertext too short"); + + const unsigned char* p = enc.data(); + + // 1. Parse ephemeral pubkey + CPubKey eph_pub(std::vector(p, p + PUBKEY_LEN)); + if (!eph_pub.IsValid()) throw key_error("Invalid ephemeral pubkey"); + p += PUBKEY_LEN; + + const unsigned char* nonce = p; + p += NONCE_LEN; + + size_t ct_len = enc.size() - PUBKEY_LEN - NONCE_LEN - TAG_LEN; + const unsigned char* ciphertext = p; + const unsigned char* tag = p + ct_len; - ctx->cipher = EVP_aes_128_cbc(); - ctx->md = EVP_ripemd160(); - ctx->kdf_md = EVP_ripemd160(); - ctx->stored_key_length = 33; - ctx->user_key = pkey; + // 2. ECDH + unsigned char shared[32]; - if(!EC_KEY_get0_private_key(ctx->user_key)) - throw(key_error("Invalid private key")); + EVP_PKEY* priv = this->GetEVPPrivKey(); + EVP_PKEY* pub = eph_pub.GetEVPPubKey(); - size_t key_length = ctx->stored_key_length; - size_t mac_length = EVP_MD_size(ctx->md); + if (!ECDH_Derive(priv, pub, shared)) { + EVP_PKEY_free(priv); + EVP_PKEY_free(pub); + throw key_error("ECDH failed"); + } + + EVP_PKEY_free(priv); + EVP_PKEY_free(pub); + + // 3. Derive key + unsigned char aead_key[32]; + if (!HKDF_SHA256(shared, sizeof(shared), aead_key, sizeof(aead_key))) + throw key_error("HKDF failed"); + + // 4. Decrypt + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if (!ctx) throw key_error("Cipher ctx alloc failed"); + + out.resize(ct_len); + int len = 0; - cryptex = secure_alloc(key_length, mac_length, encdata.size() - key_length - mac_length); - memcpy(secure_key_data(cryptex), &encdata[0], encdata.size()); - decrypted = ecies_decrypt(ctx, cryptex, &length, error); + EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, nullptr); + EVP_DecryptInit_ex(ctx, nullptr, nullptr, aead_key, nonce); - secure_free(cryptex); - delete ctx; + static const unsigned char aad[] = "AES-256-GCM|secp256k1|HKDF-SHA256|v1"; - if(!decrypted) { - throw(key_error(std::string("Error in decryption: ") + error)); + EVP_DecryptUpdate(ctx, nullptr, &len, aad, sizeof(aad) - 1); + EVP_DecryptUpdate(ctx, nullptr, &len, enc.data(), PUBKEY_LEN); + + EVP_DecryptUpdate(ctx, out.data(), &len, ciphertext, ct_len); + + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, TAG_LEN, (void*)tag); + + if (EVP_DecryptFinal_ex(ctx, out.data() + len, &len) <= 0) { + EVP_CIPHER_CTX_free(ctx); + throw key_error("Authentication failed"); } - plaindata.resize(length); - memcpy(&plaindata[0], decrypted, length); - free(decrypted); + EVP_CIPHER_CTX_free(ctx); + + OPENSSL_cleanse(shared, sizeof(shared)); + OPENSSL_cleanse(aead_key, sizeof(aead_key)); } diff --git a/src/key.h b/src/key.h index 871cbcf9..62e89872 100644 --- a/src/key.h +++ b/src/key.h @@ -6,14 +6,6 @@ #ifndef KEY_H #define KEY_H -#include -#include -#include - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) -#include // for EC_KEY definition -#endif - #include "allocators.h" #include "serialize.h" #include "util.h" @@ -41,128 +33,131 @@ // see www.keylength.com // script supports up to 75 for single byte push -class key_error : public std::runtime_error -{ -public: - explicit key_error(const std::string& str) : std::runtime_error(str) {} +/* ---------- Errors ---------- */ + +class key_error : public std::runtime_error { + public: + explicit key_error(const std::string &str) : std::runtime_error(str) {} }; -/** A reference to a CKey: the Hash160 of its serialized public key */ -class CKeyID : public uint160 -{ -public: - CKeyID() : uint160(0) { } - CKeyID(const uint160 &in) : uint160(in) { } +/* ---------- Key identifiers ---------- */ + +class CKeyID : public uint160 { + public: + CKeyID() : uint160(0) {} + CKeyID(const uint160 &in) : uint160(in) {} }; -/** A reference to a CScript: the Hash160 of its serialization (see script.h) */ -class CScriptID : public uint160 -{ -public: - CScriptID() : uint160(0) { } - CScriptID(const uint160 &in) : uint160(in) { } +class CScriptID : public uint160 { + public: + CScriptID() : uint160(0) {} + CScriptID(const uint160 &in) : uint160(in) {} }; -/* An encapsulated public key */ +/* ---------- Public key ---------- */ + class CPubKey { -private: - std::vector vchPubKey; + private: + EVP_PKEY *pkey; + std::vector vchPubKey; friend class CKey; -public: - CPubKey() { } - CPubKey(const std::vector &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { } - friend bool operator==(const CPubKey &a, const CPubKey &b) { return(a.vchPubKey == b.vchPubKey); } - friend bool operator!=(const CPubKey &a, const CPubKey &b) { return(a.vchPubKey != b.vchPubKey); } - friend bool operator<(const CPubKey &a, const CPubKey &b) { return(a.vchPubKey < b.vchPubKey); } + public: + EVP_PKEY *GetEVPPubKey() const; + CPubKey() {} + CPubKey(const std::vector &vchPubKeyIn) + : vchPubKey(vchPubKeyIn) {} + friend bool operator==(const CPubKey &a, const CPubKey &b) { + return a.vchPubKey == b.vchPubKey; + } + friend bool operator!=(const CPubKey &a, const CPubKey &b) { + return a.vchPubKey != b.vchPubKey; + } + friend bool operator<(const CPubKey &a, const CPubKey &b) { + return a.vchPubKey < b.vchPubKey; + } - IMPLEMENT_SERIALIZE( - READWRITE(vchPubKey); - ) + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey);) - CKeyID GetID() const { - return(CKeyID(Hash160(vchPubKey))); - } + CKeyID GetID() const { return CKeyID(Hash160(vchPubKey)); } - uint256 GetHash() const { - return(Hash(vchPubKey.begin(), vchPubKey.end())); - } + uint256 GetHash() const { return Hash(vchPubKey.begin(), vchPubKey.end()); } bool IsValid() const { - return((vchPubKey.size() == 33) || (vchPubKey.size() == 65)); + return vchPubKey.size() == 33 || vchPubKey.size() == 65; } - bool IsCompressed() const { - return(vchPubKey.size() == 33); - } + bool IsCompressed() const { return vchPubKey.size() == 33; } - std::vector Raw() const { - return(vchPubKey); - } + std::vector Raw() const { return vchPubKey; } - void EncryptData(const std::vector &plaindata, std::vector &encdata); + void EncryptData(const std::vector &plaintext, + std::vector &out); }; +/* ---------- Private key types ---------- */ -// secure_allocator is defined in allocators.h -// CPrivKey is a serialized private key, with all parameters included (279 bytes) +// Serialized private key typedef std::vector > CPrivKey; -// CSecret is a serialization of just the secret parameter (32 bytes) + +// Raw 32-byte secret typedef std::vector > CSecret; -/* An encapsulated OpenSSL Elliptic Curve key (public and/or private) */ +/* ---------- Private key ---------- */ + class CKey { -protected: - EC_KEY *pkey; + private: + CSecret vchSecret; + CPubKey vchPubKey; + + protected: + EVP_PKEY *pkey; bool fSet; bool fCompressedPubKey; -public: - void SetCompressedPubKey(bool fCompressed = true); - void Reset(); - - CKey(); - CKey(const CKey &b); - - CKey &operator=(const CKey &b); - - ~CKey(); + void SetCompressedPubKey(); + + public: + EVP_PKEY *GetEVPPrivKey() const; + CKey() : pkey(nullptr), fSet(false), fCompressedPubKey(false) {} + ~CKey() { + if (!vchSecret.empty()) { + OPENSSL_cleanse(vchSecret.data(), vchSecret.size()); + } + if (pkey) { + EVP_PKEY_free(pkey); + pkey = nullptr; + } + } + void Reset() { + if (pkey) EVP_PKEY_free(pkey); + pkey = nullptr; + fCompressedPubKey = false; + fSet = false; + } bool IsNull() const; bool IsCompressed() const; - void MakeNewKey(bool fCompressed); bool SetPrivKey(const CPrivKey &vchPrivKey); bool SetSecret(const CSecret &vchSecret, bool fCompressed = false); CSecret GetSecret(bool &fCompressed) const; CPrivKey GetPrivKey() const; - bool SetPubKey(const CPubKey &vchPubKey); + bool SetPubKey(const CPubKey &vchPubKeyIn); CPubKey GetPubKey() const; - - bool Sign(uint256 hash, std::vector &vchSig); - - // create a compact signature (65 bytes), which allows reconstructing the used public key - // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. - // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, - // 0x1D = second key with even y, 0x1E = second key with odd y - bool SignCompact(uint256 hash, std::vector &vchSig); - - // reconstruct public key from a compact signature - // This is only slightly more CPU intensive than just verifying it. - // If this function succeeds, the recovered public key is guaranteed to be valid - // (the signature is a valid signature of the given data for that key) - bool SetCompactSignature(uint256 hash, const std::vector &vchSig); - - bool Verify(uint256 hash, const std::vector &vchSig); - - // Verify a compact signature - bool VerifyCompact(uint256 hash, const std::vector &vchSig); - - bool IsValid(); - - void EncryptData(const std::vector &plaindata, std::vector &encdata); - - void DecryptData(const std::vector &encdata, std::vector &plaindata); + bool Sign(const uint256 hash, std::vector &vchSig) const; + bool SignCompact(const uint256 &hash, + std::vector &vchSig) const; + bool SetCompactSignature(const uint256 &hash, + const std::vector &vchSig); + bool Verify(const uint256 hash, + const std::vector &vchSig) const; + bool VerifyCompact(const uint256 &hash, + const std::vector &vchSig) const; + bool IsValid() const; + + void DecryptData(const std::vector &enc, + std::vector &out); }; #endif /* KEY_H */ diff --git a/src/netbase.cpp b/src/netbase.cpp index 28f6ffd8..b84b6403 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -912,7 +912,11 @@ std::vector CNetAddr::GetGroup() const nBits -= 8; } if (nBits > 0) - vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); + { + uint8_t byte = GetByte(15 - nStartByte); + byte &= static_cast(0xFF << (8 - nBits)); + vchRet.push_back(byte); + } return vchRet; } diff --git a/src/rpcmain.cpp b/src/rpcmain.cpp index 4104415e..2357f0cd 100644 --- a/src/rpcmain.cpp +++ b/src/rpcmain.cpp @@ -5,9 +5,12 @@ #include #include +#include +#include #include #include +#include #if (BOOST_VERSION >= 107100) #include #else @@ -35,6 +38,7 @@ using namespace std; using namespace boost; using namespace boost::asio; +using namespace boost::asio::ip; using namespace json_spirit; extern CWallet *pwalletMain; @@ -558,8 +562,44 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) stream << HTTPReply(nStatus, strReply, false) << std::flush; } -bool ClientAllowed(const boost::asio::ip::address& address) -{ +bool ClientAllowed(const boost::asio::ip::address& address) { +#if (BOOST_VERSION >= 108700) + if (address.is_v6()) { + auto v6_addr = address.to_v6(); + + if (v6_addr.is_v4_mapped()) { + // Manually extract the last 4 bytes for IPv4 + auto bytes = v6_addr.to_bytes(); + + // The IPv4 address is stored in the last four bytes + address_v4::bytes_type v4_bytes = { + bytes[12], bytes[13], bytes[14], bytes[15] + }; + return ClientAllowed(address_v4(v4_bytes)); + } + } + + if (address == address_v4::loopback() || address == address_v6::loopback()) { + return true; + } + + if (address.is_v4()) { + // Check whether IPv4 address matches 127.0.0.0/8 (loopback subnet) + auto v4_addr = address.to_v4(); + if ((v4_addr.to_uint() & 0xff000000) == 0x7f000000) { + return true; + } + } + + const std::string strAddress = address.to_string(); + const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; + for (const auto& strAllow : vAllow) { + if (WildcardMatch(strAddress, strAllow)) + return true; + } + + return false; +#else // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses if (address.is_v6() && (address.to_v6().is_v4_compatible() @@ -579,8 +619,10 @@ bool ClientAllowed(const boost::asio::ip::address& address) if (WildcardMatch(strAddress, strAllow)) return true; return false; +#endif } + // // IOStream device that speaks SSL but can also speak non-SSL // @@ -617,6 +659,18 @@ class SSLIOStreamDevice : public iostreams::device { #else ip::tcp::resolver resolver(stream.get_io_service()); #endif + +#if (BOOST_VERSION >= 108700) + ip::tcp::resolver::results_type endpoints = resolver.resolve(server.c_str(), port.c_str()); + ip::tcp::resolver::results_type end; + + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoints != end) + { + stream.lowest_layer().close(); + boost::asio::connect(stream.lowest_layer(), endpoints, error); + } +#else ip::tcp::resolver::query query(server.c_str(), port.c_str()); ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); ip::tcp::resolver::iterator end; @@ -626,6 +680,7 @@ class SSLIOStreamDevice : public iostreams::device { stream.lowest_layer().close(); stream.lowest_layer().connect(*endpoint_iterator++, error); } +#endif if (error) return false; return true; @@ -652,10 +707,17 @@ class AcceptedConnectionImpl : public AcceptedConnection { public: AcceptedConnectionImpl( +#if (BOOST_VERSION >= 107000) + asio::io_context& io_context, + ssl::context &context, + bool fUseSSL) : + sslStream(io_context, context), +#else asio::io_service& io_service, ssl::context &context, bool fUseSSL) : sslStream(io_service, context), +#endif _d(sslStream, fUseSSL), _stream(_d) { @@ -836,7 +898,11 @@ void ThreadRPCServer2(void* parg) const bool fUseSSL = GetBoolArg("-rpcssl"); +#if (BOOST_VERSION >= 107000) + asio::io_context io_context; +#else asio::io_service io_service; +#endif #if (BOOST_VERSION > 106501) ssl::context context(ssl::context::sslv23); @@ -879,7 +945,11 @@ void ThreadRPCServer2(void* parg) asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); boost::system::error_code v6_only_error; +#if (BOOST_VERSION >= 107000) + boost::shared_ptr acceptor(new ip::tcp::acceptor(io_context)); +#else boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); +#endif boost::signals2::signal StopRequests; @@ -894,7 +964,11 @@ void ThreadRPCServer2(void* parg) acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); acceptor->bind(endpoint); +#if (BOOST_VERSION >= 106600) + acceptor->listen(socket_base::max_listen_connections); +#else acceptor->listen(socket_base::max_connections); +#endif RPCListen(acceptor, context, fUseSSL); // Cancel outstanding listen-requests for this acceptor when shutting down @@ -915,12 +989,19 @@ void ThreadRPCServer2(void* parg) { bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); endpoint.address(bindAddress); - +#if (BOOST_VERSION >= 107000) + acceptor.reset(new ip::tcp::acceptor(io_context)); +#else acceptor.reset(new ip::tcp::acceptor(io_service)); +#endif acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor->bind(endpoint); +#if (BOOST_VERSION >= 106600) + acceptor->listen(socket_base::max_listen_connections); +#else acceptor->listen(socket_base::max_connections); +#endif RPCListen(acceptor, context, fUseSSL); // Cancel outstanding listen-requests for this acceptor when shutting down @@ -944,7 +1025,11 @@ void ThreadRPCServer2(void* parg) vnThreadsRunning[THREAD_RPCLISTENER]--; while (!fShutdown) +#if (BOOST_VERSION >= 107000) + io_context.run_one(); +#else io_service.run_one(); +#endif vnThreadsRunning[THREAD_RPCLISTENER]++; StopRequests(); } @@ -1163,7 +1248,11 @@ Object CallRPC(const string &strMethod, const Array ¶ms) { // Connect to localhost bool fUseSSL = GetBoolArg("-rpcssl"); +#if (BOOST_VERSION >= 107000) + asio::io_context io_context; +#else asio::io_service io_service; +#endif #if (BOOST_VERSION > 106501) ssl::context context(ssl::context::sslv23); @@ -1172,7 +1261,11 @@ Object CallRPC(const string &strMethod, const Array ¶ms) { #endif context.set_options(ssl::context::no_sslv2); +#if (BOOST_VERSION >= 107000) + asio::ssl::stream sslStream(io_context, context); +#else asio::ssl::stream sslStream(io_service, context); +#endif SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream< SSLIOStreamDevice > stream(d); if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) diff --git a/src/script.cpp b/src/script.cpp index b6a4f68f..f75a8399 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "sync.h" #include "bignum.h" #include "keystore.h" @@ -821,8 +823,27 @@ bool EvalScript(vector >& stack, const CScript& script, co return(false); valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); - if(opcode == OP_RIPEMD160) - RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + if(opcode == OP_RIPEMD160) { + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + if(!ctx) + return false; + const EVP_MD* md = EVP_ripemd160(); + + if(1 != EVP_DigestInit_ex(ctx, md, NULL)) { + EVP_MD_CTX_free(ctx); + return false; + } + if(1 != EVP_DigestUpdate(ctx, &vch[0], vch.size())) { + EVP_MD_CTX_free(ctx); + return false; + } + unsigned int hash_len = 0; + if(1 != EVP_DigestFinal_ex(ctx, &vchHash[0], &hash_len)) { + EVP_MD_CTX_free(ctx); + return false; + } + EVP_MD_CTX_free(ctx); + } else if(opcode == OP_SHA1) SHA1(&vch[0], vch.size(), &vchHash[0]); else if(opcode == OP_SHA256) diff --git a/src/sync.h b/src/sync.h index 0c3a4b6b..614a77a3 100644 --- a/src/sync.h +++ b/src/sync.h @@ -15,7 +15,7 @@ typedef boost::recursive_mutex CCriticalSection; /** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef boost::mutex::scoped_lock CWaitableCriticalSection; +typedef boost::mutex CWaitableCriticalSection; #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); diff --git a/src/uint256.h b/src/uint256.h index 37504697..02740d3f 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -334,7 +334,7 @@ class base_uint return (GetHex()); } - unsigned char* begin() + unsigned char* begin() const { return (unsigned char*)&pn[0]; } diff --git a/src/util.h b/src/util.h index 083ed5fa..6ae7d526 100644 --- a/src/util.h +++ b/src/util.h @@ -10,6 +10,7 @@ #include #include #include +#include // for memcpy #include #include @@ -17,6 +18,7 @@ #include #include +#include #include #include @@ -382,31 +384,53 @@ inline uint256 Hash(const T1 pbegin, const T1 pend) class CHashWriter { private: - SHA256_CTX ctx; + EVP_MD_CTX* ctx; // EVP message digest context public: int nType; int nVersion; void Init() { - SHA256_Init(&ctx); + ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); } CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { Init(); } + ~CHashWriter() { + if (ctx) { + EVP_MD_CTX_free(ctx); + } + } + CHashWriter& write(const char *pch, size_t size) { - SHA256_Update(&ctx, pch, size); + EVP_DigestUpdate(ctx, pch, size); return (*this); } // invalidates the object uint256 GetHash() { + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int length = 0; + + EVP_DigestFinal_ex(ctx, hash, &length); + + // Re-initialize context to avoid reuse issues + EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); + uint256 hash1; - SHA256_Final((unsigned char*)&hash1, &ctx); + memcpy(&hash1, hash, sizeof(hash1)); + + // Double SHA256 as in original uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + EVP_MD_CTX* ctx2 = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx2, EVP_sha256(), nullptr); + EVP_DigestUpdate(ctx2, &hash1, sizeof(hash1)); + EVP_DigestFinal_ex(ctx2, (unsigned char*)&hash2, &length); + EVP_MD_CTX_free(ctx2); + return hash2; } @@ -418,23 +442,40 @@ class CHashWriter } }; - +// Hash function for two ranges template inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end) { static unsigned char pblank[1]; uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); + + const unsigned char* p1ptr = (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]); + size_t p1len = (p1end - p1begin) * sizeof(p1begin[0]); + EVP_DigestUpdate(ctx, p1ptr, p1len); + + const unsigned char* p2ptr = (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]); + size_t p2len = (p2end - p2begin) * sizeof(p2begin[0]); + EVP_DigestUpdate(ctx, p2ptr, p2len); + + unsigned int length = 0; + EVP_DigestFinal_ex(ctx, (unsigned char*)&hash1, &length); + EVP_MD_CTX_free(ctx); + + // Double SHA256 uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + EVP_MD_CTX* ctx2 = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx2, EVP_sha256(), nullptr); + EVP_DigestUpdate(ctx2, &hash1, sizeof(hash1)); + EVP_DigestFinal_ex(ctx2, (unsigned char*)&hash2, &length); + EVP_MD_CTX_free(ctx2); + return hash2; } +// Hash function for three ranges template inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end, @@ -442,14 +483,33 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, { static unsigned char pblank[1]; uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr); + + const unsigned char* p1ptr = (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]); + size_t p1len = (p1end - p1begin) * sizeof(p1begin[0]); + EVP_DigestUpdate(ctx, p1ptr, p1len); + + const unsigned char* p2ptr = (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]); + size_t p2len = (p2end - p2begin) * sizeof(p2begin[0]); + EVP_DigestUpdate(ctx, p2ptr, p2len); + + const unsigned char* p3ptr = (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]); + size_t p3len = (p3end - p3begin) * sizeof(p3begin[0]); + EVP_DigestUpdate(ctx, p3ptr, p3len); + + unsigned int length = 0; + EVP_DigestFinal_ex(ctx, (unsigned char*)&hash1, &length); + EVP_MD_CTX_free(ctx); + + // Double SHA256 uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + EVP_MD_CTX* ctx2 = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx2, EVP_sha256(), nullptr); + EVP_DigestUpdate(ctx2, &hash1, sizeof(hash1)); + EVP_DigestFinal_ex(ctx2, (unsigned char*)&hash2, &length); + EVP_MD_CTX_free(ctx2); + return hash2; } @@ -465,8 +525,40 @@ inline uint160 Hash160(const std::vector& vch) { uint256 hash1; SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; - RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + unsigned int len = 0; + + // Initialize EVP_MD_CTX + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + if (!ctx) + { + // handle error + throw std::runtime_error("Failed to create EVP_MD_CTX"); + } + + // Initialize digest context for RIPEMD160 + if (1 != EVP_DigestInit_ex(ctx, EVP_ripemd160(), NULL)) + { + EVP_MD_CTX_free(ctx); + throw std::runtime_error("EVP_DigestInit_ex failed"); + } + + // Perform digest + if (1 != EVP_DigestUpdate(ctx, &hash1, sizeof(hash1))) + { + EVP_MD_CTX_free(ctx); + throw std::runtime_error("EVP_DigestUpdate failed"); + } + + // Finalize digest + if (1 != EVP_DigestFinal_ex(ctx, (unsigned char*)&hash2, &len)) + { + EVP_MD_CTX_free(ctx); + throw std::runtime_error("EVP_DigestFinal_ex failed"); + } + + EVP_MD_CTX_free(ctx); return hash2; } diff --git a/src/wallet.cpp b/src/wallet.cpp index 7da6fc28..5965a9e9 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -4,6 +4,8 @@ // file LICENCE or http://opensource.org/license/mit #include +#include +#include #include "base58.h" #include "crypter.h" @@ -1126,7 +1128,9 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe vector > > vValue; int64 nTotalLower = 0; - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(vCoins.begin(), vCoins.end(), g); BOOST_FOREACH(COutput output, vCoins) {