From: 0xDEADFACE Date: Sat, 13 Feb 2016 22:52:28 +0000 (+0300) Subject: Merge pull request #272 from svost/V668 X-Git-Tag: nvc-v0.5.6~81^2~2 X-Git-Url: https://git.novaco.in/?a=commitdiff_plain;h=a058e8e1f1f88024c439a3a5b41bd40b3b8198cd;hp=31e9cb26642187e571f178ddcfa268ce7c4e56cc;p=novacoin.git Merge pull request #272 from svost/V668 Fix V668 PVS Studio - deal with pointer and NULL --- diff --git a/doc/MalleableKeys.txt b/doc/MalleableKeys.txt new file mode 100644 index 0000000..357ef95 --- /dev/null +++ b/doc/MalleableKeys.txt @@ -0,0 +1,34 @@ +> newmalleablekey +{ +"PrivatePair" : "DfAmbLTs7VDvUor5YF1gU5K5CGsHWezFh9htgiJBbmd3Z48H2Cu3NpYjSDQ1qaKvMGctkCgyqLCiEJFNCEBNxhhwLpquXDAa", +"PublicPair" : "5GRSi9q1XUVBYxifTr5D5bQUKFvyfWc8GFH8eLUqsE9fMYRtn2WhnNjNFpbaZBrypP3E9jMUYGwya7UM3Vfs8UqkqWTxiut4rKU" +} + +> adjustmalleablepubkey 5GRSi9q1XUVBYxifTr5D5bQUKFvyfWc8GFH8eLUqsE9fMYRtn2WhnNjNFpbaZBrypP3E9jMUYGwya7UM3Vfs8UqkqWTxiut4rKU +{ +"R" : "022f010598ec5687f719edce97f6ba7e32fc34c7d7b45ba49de115bb670210f59f", +"PubkeyVariant" : "03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7", +"KeyVariantID" : "4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB" +} + +> adjustmalleablekey DfAmbLTs7VDvUor5YF1gU5K5CGsHWezFh9htgiJBbmd3Z48H2Cu3NpYjSDQ1qaKvMGctkCgyqLCiEJFNCEBNxhhwLpquXDAa 03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7 022f010598ec5687f719edce97f6ba7e32fc34c7d7b45ba49de115bb670210f59f +{ +"PrivateKey" : "MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K" +} + +> importprivkey MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K "test" + +> validateaddress 4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB +{ +"isvalid" : true, +"address" : "4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB", +"ismine" : true, +"watchonly" : false, +"isscript" : false, +"pubkey" : "03105ec5214ce118828cfb08a349b04f336ed4eae50995dbe5268112748008fdf7", +"iscompressed" : true, +"account" : "test" +} + +> dumpprivkey 4UcaTGRRNWiH2xs6tTFHwtVzjS1BZrMLrB +MDhzYBXJHrkaoDVSnJjaxzHVYURyAgVzqEhft2oeMhyfww1pDW6K diff --git a/novacoin-qt.pro b/novacoin-qt.pro index a0852e5..1759e98 100644 --- a/novacoin-qt.pro +++ b/novacoin-qt.pro @@ -259,7 +259,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/multisiginputentry.h \ src/qt/multisigdialog.h \ src/qt/secondauthdialog.h \ - src/qt/qrcodedialog.h + src/qt/qrcodedialog.h \ + src/ies.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/intro.cpp \ @@ -338,7 +339,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/multisigdialog.cpp \ src/qt/secondauthdialog.cpp \ src/qt/qrcodedialog.cpp \ - src/base58.cpp + src/base58.cpp \ + src/cryptogram.cpp \ + src/ecies.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index b10cee7..92042d0 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -316,6 +316,10 @@ static const CRPCCommand vRPCCommands[] = { "repairwallet", &repairwallet, false, true}, { "resendtx", &resendtx, false, true}, { "makekeypair", &makekeypair, false, true}, + { "newmalleablekey", &newmalleablekey, false, false}, + { "adjustmalleablekey", &adjustmalleablekey, false, false}, + { "adjustmalleablepubkey", &adjustmalleablepubkey, false, false}, + { "listmalleablepubkeys", &listmalleablepubkeys, false, false}, { "sendalert", &sendalert, false, false}, }; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index dcf0863..226007b 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -206,6 +206,10 @@ extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fH extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value mergecoins(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value newmalleablekey(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value adjustmalleablekey(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value adjustmalleablepubkey(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listmalleablepubkeys(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/cryptogram.cpp b/src/cryptogram.cpp new file mode 100644 index 0000000..c107b44 --- /dev/null +++ b/src/cryptogram.cpp @@ -0,0 +1,65 @@ +/** + * @file /cryptron/secure.c + * + * @brief Functions for handling the secure data type. + * + * $Author: Ladar Levison $ + * $Website: http://lavabit.com $ + * + */ + +#include "ies.h" +#define HEADSIZE (sizeof(cryptogram_head_t)) + +size_t cryptogram_key_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.key; +} + +size_t cryptogram_mac_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.mac; +} + +size_t cryptogram_body_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.body; +} + +size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (head->length.key + head->length.mac + head->length.body); +} + +size_t cryptogram_total_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return HEADSIZE + (head->length.key + head->length.mac + head->length.body); +} + +unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram) { + return (unsigned char *)cryptogram + HEADSIZE; +} + +unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (unsigned char *)cryptogram + (HEADSIZE + head->length.key + head->length.body); +} + +unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (unsigned char *)cryptogram + (HEADSIZE + head->length.key); +} + +cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body) { + cryptogram_t *cryptogram = (cryptogram_t *) malloc(HEADSIZE + key + mac + body); + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + head->length.key = key; + head->length.mac = mac; + head->length.body = body; + return cryptogram; +} + +void cryptogram_free(cryptogram_t *cryptogram) { + free(cryptogram); + return; +} diff --git a/src/ecies.cpp b/src/ecies.cpp new file mode 100644 index 0000000..215d390 --- /dev/null +++ b/src/ecies.cpp @@ -0,0 +1,568 @@ +/** + * @file /cryptron/ecies.c + * + * @brief ECIES encryption/decryption functions. + * + * $Author: Ladar Levison $ + * $Website: http://lavabit.com $ + * + */ + +#include "ies.h" +#include +#include +#include + +#define SET_ERROR(string) \ + sprintf(error, "%s %s:%d", (string), __FILE__, __LINE__) +#define SET_OSSL_ERROR(string) \ + sprintf(error, "%s {error = %s} %s:%d", (string), ERR_error_string(ERR_get_error(), NULL), __FILE__, __LINE__) + +/* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + * Taken from openssl/crypto/ecdh/ech_kdf.c in github:openssl/openssl + * ffa08b3242e0f10f1fef3c93ef3f0b51de8c27a9 */ + +/* 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) +{ + EVP_MD_CTX mctx; + int rv = 0; + unsigned int i; + size_t mdlen; + unsigned char ctr[4]; + 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]; + EVP_DigestInit_ex(&mctx, md, NULL); + 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 == 0) + 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; +} + +static size_t envelope_key_len(const ies_ctx_t *ctx) +{ + return EVP_CIPHER_key_length(ctx->cipher) + EVP_MD_size(ctx->md); +} + +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())) { + SET_OSSL_ERROR("EC_KEY_new failed"); + return NULL; + } + + if (!(group = EC_KEY_get0_group(user))) { + SET_ERROR("The user key does not have group"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_group(key, group) != 1) { + SET_OSSL_ERROR("EC_KEY_set_group failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_generate_key(key) != 1) { + SET_OSSL_ERROR("EC_KEY_generate_key failed"); + EC_KEY_free(key); + return NULL; + } + + return key; +} + +static unsigned char *prepare_envelope_key(const ies_ctx_t *ctx, cryptogram_t *cryptogram, char *error) +{ + + const size_t key_buf_len = envelope_key_len(ctx); + const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; + unsigned char *envelope_key = NULL, *ktmp = NULL; + EC_KEY *ephemeral = NULL; + size_t written_length; + + /* High-level ECDH via EVP does not allow use of arbitrary KDF function. + * We should use low-level API for KDF2 + * c.f. openssl/crypto/ec/ec_pmeth.c */ + if ((envelope_key = (unsigned char *) OPENSSL_malloc(key_buf_len)) == NULL) { + SET_ERROR("Failed to allocate memory for envelope_key"); + goto err; + } + + if (!(ephemeral = ecies_key_create(ctx->user_key, error))) { + goto err; + } + + /* key agreement and KDF + * reference: openssl/crypto/ec/ec_pmeth.c */ + ktmp = (unsigned char *) OPENSSL_malloc(ecdh_key_len); + if (ktmp == NULL) { + SET_ERROR("No memory for ECDH temporary key"); + goto err; + } + + if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ctx->user_key), ephemeral, NULL) + != (int)ecdh_key_len) { + SET_OSSL_ERROR("An error occurred while ECDH_compute_key"); + goto err; + } + + /* equals to ISO 18033-2 KDF2 */ + if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) { + SET_OSSL_ERROR("Failed to stretch with KDF2"); + goto err; + } + + /* Store the public key portion of the ephemeral key. */ + written_length = EC_POINT_point2oct( + EC_KEY_get0_group(ephemeral), + EC_KEY_get0_public_key(ephemeral), + POINT_CONVERSION_COMPRESSED, + cryptogram_key_data(cryptogram), + ctx->stored_key_length, + NULL); + if (written_length == 0) { + SET_OSSL_ERROR("Error while recording the public portion of the envelope key"); + goto err; + } + if (written_length != ctx->stored_key_length) { + SET_ERROR("Written envelope key length does not match with expected"); + goto err; + } + + EC_KEY_free(ephemeral); + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + + return envelope_key; + + err: + if (ephemeral) + EC_KEY_free(ephemeral); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, key_buf_len); + OPENSSL_free(envelope_key); + } + if (ktmp) { + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + } + return NULL; +} + +static int store_cipher_body( + const ies_ctx_t *ctx, + const unsigned char *envelope_key, + const unsigned char *data, + size_t length, + cryptogram_t *cryptogram, + char *error) +{ + int out_len, len_sum = 0; + size_t expected_len = cryptogram_body_length(cryptogram); + unsigned char iv[EVP_MAX_IV_LENGTH]; + EVP_CIPHER_CTX cipher; + unsigned char *body; + + /* For now we use an empty initialization vector. */ + memset(iv, 0, EVP_MAX_IV_LENGTH); + + EVP_CIPHER_CTX_init(&cipher); + body = cryptogram_body_data(cryptogram); + + if (EVP_EncryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1 + || EVP_EncryptUpdate(&cipher, body, &out_len, data, length) != 1) { + SET_OSSL_ERROR("Error while trying to secure the data using the symmetric cipher"); + EVP_CIPHER_CTX_cleanup(&cipher); + return 0; + } + + if (expected_len < (size_t)out_len) { + SET_ERROR("The symmetric cipher overflowed"); + EVP_CIPHER_CTX_cleanup(&cipher); + return 0; + } + + body += out_len; + len_sum += out_len; + if (EVP_EncryptFinal_ex(&cipher, body, &out_len) != 1) { + SET_OSSL_ERROR("Error while finalizing the data using the symmetric cipher"); + EVP_CIPHER_CTX_cleanup(&cipher); + cryptogram_free(cryptogram); + return 0; + } + + EVP_CIPHER_CTX_cleanup(&cipher); + + if (expected_len < (size_t)len_sum) { + SET_ERROR("The symmetric cipher overflowed"); + return 0; + } + + return 1; +} + +static int store_mac_tag(const ies_ctx_t *ctx, const unsigned char *envelope_key, cryptogram_t *cryptogram, char *error) { + 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 = cryptogram_mac_length(cryptogram); + unsigned int out_len; + HMAC_CTX hmac; + + HMAC_CTX_init(&hmac); + + /* Generate hash tag using encrypted data */ + if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1 + || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1 + || HMAC_Final(&hmac, cryptogram_mac_data(cryptogram), &out_len) != 1) { + SET_OSSL_ERROR("Unable to generate tag"); + HMAC_CTX_cleanup(&hmac); + return 0; + } + + HMAC_CTX_cleanup(&hmac); + + if (out_len != mac_length) { + SET_ERROR("MAC length expectation does not meet"); + return 0; + } + + return 1; +} + +cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error) { + + const size_t block_length = EVP_CIPHER_block_size(ctx->cipher); + const size_t mac_length = EVP_MD_size(ctx->md); + cryptogram_t *cryptogram = NULL; + unsigned char *envelope_key = NULL; + + if (!ctx || !data || !length) { + SET_ERROR("Invalid arguments"); + return NULL; + } + + if (block_length == 0 || block_length > EVP_MAX_BLOCK_LENGTH) { + SET_ERROR("Derived block size is incorrect"); + return NULL; + } + + cryptogram = cryptogram_alloc(ctx->stored_key_length, + mac_length, + length + (length % block_length ? (block_length - (length % block_length)) : 0)); + if (!cryptogram) { + SET_ERROR("Unable to allocate a cryptogram_t buffer to hold the encrypted result."); + goto err; + } + + if ((envelope_key = prepare_envelope_key(ctx, cryptogram, error)) == NULL) { + goto err; + } + + if (!store_cipher_body(ctx, envelope_key, data, length, cryptogram, error)) { + goto err; + } + + if (!store_mac_tag(ctx, envelope_key, cryptogram, error)) { + goto err; + } + + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + + return cryptogram; + + err: + if (cryptogram) + cryptogram_free(cryptogram); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + } + return NULL; +} + +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())) { + SET_OSSL_ERROR("Cannot create instance for ephemeral key"); + return NULL; + } + + if (!(group = EC_KEY_get0_group(user))) { + SET_ERROR("Cannot get group from user key"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_group(key, group) != 1) { + SET_OSSL_ERROR("EC_KEY_set_group failed"); + EC_KEY_free(key); + return NULL; + } + + if (!(point = EC_POINT_new(group))) { + SET_OSSL_ERROR("EC_POINT_new failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_POINT_oct2point(group, point, octets, length, NULL) != 1) { + SET_OSSL_ERROR("EC_POINT_oct2point failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_public_key(key, point) != 1) { + SET_OSSL_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) { + SET_OSSL_ERROR("EC_KEY_check_key failed"); + EC_KEY_free(key); + return NULL; + } + + return key; +} + +unsigned char *restore_envelope_key(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, char *error) +{ + + const size_t key_buf_len = envelope_key_len(ctx); + const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; + EC_KEY *ephemeral = NULL, *user_copy = NULL; + unsigned char *envelope_key = NULL, *ktmp = NULL; + + if ((envelope_key = (unsigned char *) OPENSSL_malloc(key_buf_len)) == NULL) { + SET_ERROR("Failed to allocate memory for envelope_key"); + goto err; + } + + if (!(user_copy = EC_KEY_new())) { + SET_OSSL_ERROR("Failed to create instance for user key copy"); + goto err; + } + + if (!(EC_KEY_copy(user_copy, ctx->user_key))) { + SET_OSSL_ERROR("Failed to copy user key"); + goto err; + } + + if (!(ephemeral = ecies_key_create_public_octets(user_copy, cryptogram_key_data(cryptogram), cryptogram_key_length(cryptogram), error))) { + goto err; + } + + /* key agreement and KDF + * reference: openssl/crypto/ec/ec_pmeth.c */ + ktmp = (unsigned char *) OPENSSL_malloc(ecdh_key_len); + if (ktmp == NULL) { + SET_ERROR("No memory for ECDH temporary key"); + goto err; + } + + if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ephemeral), user_copy, NULL) + != (int)ecdh_key_len) { + SET_OSSL_ERROR("An error occurred while ECDH_compute_key"); + goto err; + } + + /* equals to ISO 18033-2 KDF2 */ + if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) { + SET_OSSL_ERROR("Failed to stretch with KDF2"); + goto err; + } + + EC_KEY_free(user_copy); + EC_KEY_free(ephemeral); + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + + return envelope_key; + + err: + if (ephemeral) + EC_KEY_free(ephemeral); + if (user_copy) + EC_KEY_free(user_copy); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, key_buf_len); + OPENSSL_free(envelope_key); + } + if (ktmp) { + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + } + return NULL; +} + +static int verify_mac(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char * envelope_key, char *error) +{ + 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 = cryptogram_mac_length(cryptogram); + unsigned int out_len; + HMAC_CTX hmac; + unsigned char md[EVP_MAX_MD_SIZE]; + + HMAC_CTX_init(&hmac); + + /* Generate hash tag using encrypted data */ + if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1 + || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1 + || HMAC_Final(&hmac, md, &out_len) != 1) { + SET_OSSL_ERROR("Unable to generate tag"); + HMAC_CTX_cleanup(&hmac); + return 0; + } + + HMAC_CTX_cleanup(&hmac); + + if (out_len != mac_length) { + SET_ERROR("MAC length expectation does not meet"); + return 0; + } + + if (memcmp(md, cryptogram_mac_data(cryptogram), mac_length) != 0) { + SET_ERROR("MAC tag verification failed"); + return 0; + } + + return 1; +} + +unsigned char *decrypt_body(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char *envelope_key, size_t *length, char *error) +{ + int out_len; + size_t output_sum; + const size_t body_length = cryptogram_body_length(cryptogram); + unsigned char iv[EVP_MAX_IV_LENGTH], *block, *output; + EVP_CIPHER_CTX cipher; + + if (!(output = (unsigned char*)malloc(body_length + 1))) { + SET_ERROR("Failed to allocate memory for clear text"); + return NULL; + } + + /* For now we use an empty initialization vector */ + memset(iv, 0, EVP_MAX_IV_LENGTH); + memset(output, 0, body_length + 1); + + EVP_CIPHER_CTX_init(&cipher); + + block = output; + if (EVP_DecryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1 + || EVP_DecryptUpdate(&cipher, block, &out_len, cryptogram_body_data(cryptogram), body_length) != 1) { + SET_OSSL_ERROR("Unable to decrypt"); + EVP_CIPHER_CTX_cleanup(&cipher); + free(output); + return NULL; + } + output_sum = out_len; + + block += output_sum; + if (EVP_DecryptFinal_ex(&cipher, block, &out_len) != 1) { + printf("Unable to decrypt the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_cleanup(&cipher); + free(output); + return NULL; + } + output_sum += out_len; + + EVP_CIPHER_CTX_cleanup(&cipher); + + *length = output_sum; + + return output; +} + +unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error) +{ + unsigned char *envelope_key = NULL, *output = NULL; + + if (!ctx || !cryptogram || !length || !error) { + SET_ERROR("Invalid argument"); + goto err; + } + + envelope_key = restore_envelope_key(ctx, cryptogram, error); + if (envelope_key == NULL) { + goto err; + } + + if (!verify_mac(ctx, cryptogram, envelope_key, error)) { + goto err; + } + + if ((output = decrypt_body(ctx, cryptogram, envelope_key, length, error)) == NULL) { + goto err; + } + + err: + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + + return output; +} + +ies_ctx_t *create_context(EC_KEY *user_key) +{ + ies_ctx_t* ctx = (ies_ctx_t*) malloc(sizeof(ies_ctx_t)); + ctx->cipher = EVP_aes_128_cbc(); + ctx->md = EVP_sha1(); + ctx->kdf_md = EVP_sha1(); + ctx->stored_key_length = 33; + ctx->user_key = user_key; + + return ctx; +} diff --git a/src/ies.h b/src/ies.h new file mode 100644 index 0000000..bf69a82 --- /dev/null +++ b/src/ies.h @@ -0,0 +1,44 @@ +/** + * reference: ecies.h + */ + +#ifndef _IES_H_ +#define _IES_H_ + +#include +#include +#include + +typedef struct { + const EVP_CIPHER *cipher; + const EVP_MD *md; /* for mac tag */ + const EVP_MD *kdf_md; /* for KDF */ + size_t stored_key_length; + const EC_KEY *user_key; +} ies_ctx_t; + +typedef struct { + struct { + size_t key; + size_t mac; + size_t body; + } length; +} cryptogram_head_t; + +typedef unsigned char * cryptogram_t; + +void cryptogram_free(cryptogram_t *cryptogram); +unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram); +unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram); +unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram); +size_t cryptogram_key_length(const cryptogram_t *cryptogram); +size_t cryptogram_mac_length(const cryptogram_t *cryptogram); +size_t cryptogram_body_length(const cryptogram_t *cryptogram); +size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram); +size_t cryptogram_total_length(const cryptogram_t *cryptogram); +cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body); +cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error); +unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error); +ies_ctx_t *create_context(EC_KEY *user_key); + +#endif /* _IES_H_ */ diff --git a/src/kernelrecord.cpp b/src/kernelrecord.cpp index a9a2e8e..9c61d06 100644 --- a/src/kernelrecord.cpp +++ b/src/kernelrecord.cpp @@ -47,7 +47,7 @@ vector KernelRecord::decomposeOutput(const CWallet *wallet, const CTxDestination address; std::string addrStr; - uint64_t coinAge = max( (txOut.nValue * nDayWeight) / (COIN * nOneDay), 0LL); + uint64_t coinAge = max( (txOut.nValue * nDayWeight) / (COIN * nOneDay), (int64_t)0); if (ExtractDestination(txOut.scriptPubKey, address)) { @@ -107,7 +107,7 @@ double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const //uint64_t coinAge = max(nValue * dayWeight / COIN, (int64_t)0); //return target * coinAge / pow(static_cast(2), 256); int64_t Weight = (min((GetAdjustedTime() - nTime) + timeOffset, (int64_t)(nStakeMinAge+nStakeMaxAge)) - nStakeMinAge); - uint64_t coinAge = max(nValue * Weight / (COIN * nOneDay), 0LL); + uint64_t coinAge = max(nValue * Weight / (COIN * nOneDay), (int64_t)0); return coinAge / (pow(static_cast(2),32) * difficulty); } diff --git a/src/key.cpp b/src/key.cpp index e95fb17..dafffcf 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -6,8 +6,12 @@ #include #include +#include +#include #include "key.h" +#include "base58.h" +#include "ies.h" // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) @@ -532,3 +536,674 @@ bool CKey::IsValid() key2.SetSecret(secret, fCompr); return GetPubKey() == key2.GetPubKey(); } + +CPoint::CPoint() +{ + std::string err; + group = NULL; + point = NULL; + ctx = NULL; + + group = EC_GROUP_new_by_curve_name(NID_secp256k1); + if (!group) { + err = "EC_KEY_new_by_curve_name failed."; + goto finish; + } + + point = EC_POINT_new(group); + if (!point) { + err = "EC_POINT_new failed."; + goto finish; + } + + ctx = BN_CTX_new(); + if (!ctx) { + err = "BN_CTX_new failed."; + goto finish; + } + + return; + +finish: + if (group) EC_GROUP_free(group); + if (point) EC_POINT_free(point); + throw std::runtime_error(std::string("CPoint::CPoint() : - ") + err); +} + +bool CPoint::operator!=(const CPoint &a) +{ + if (EC_POINT_cmp(group, point, a.point, ctx) != 0) + return true; + return false; +} +CPoint::~CPoint() +{ + if (point) EC_POINT_free(point); + if (group) EC_GROUP_free(group); + if (ctx) BN_CTX_free(ctx); +} + +// Initialize from octets stream +bool CPoint::setBytes(const std::vector &vchBytes) +{ + if (!EC_POINT_oct2point(group, point, &vchBytes[0], vchBytes.size(), ctx)) { + return false; + } + return true; +} + +// Initialize from octets stream +bool CPoint::setPubKey(const CPubKey &vchPubKey) +{ + return setBytes(vchPubKey.Raw()); +} + +// Serialize to octets stream +bool CPoint::getBytes(std::vector &vchBytes) +{ + unsigned int nSize = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); + vchBytes.resize(nSize); + if (!(nSize == EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, &vchBytes[0], nSize, ctx))) { + return false; + } + return true; +} + +// ECC multiplication by specified multiplier +bool CPoint::ECMUL(const CBigNum &bnMultiplier) +{ + if (!EC_POINT_mul(group, point, NULL, point, &bnMultiplier, NULL)) { + printf("CPoint::ECMUL() : EC_POINT_mul failed"); + return false; + } + + return true; +} + +// Calculate G*m + q +bool CPoint::ECMULGEN(const CBigNum &bnMultiplier, const CPoint &qPoint) +{ + if (!EC_POINT_mul(group, point, &bnMultiplier, qPoint.point, BN_value_one(), NULL)) { + printf("CPoint::ECMULGEN() : EC_POINT_mul failed."); + return false; + } + + return true; +} + +// CMalleablePubKey + +void CMalleablePubKey::GetVariant(CPubKey &R, CPubKey &vchPubKeyVariant) +{ + EC_KEY *eckey = NULL; + eckey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (eckey == NULL) { + throw key_error("CMalleablePubKey::GetVariant() : EC_KEY_new_by_curve_name failed"); + } + + // Use standard key generation function to get r and R values. + // + // r will be presented by private key; + // R is ECDSA public key which calculated as G*r + if (!EC_KEY_generate_key(eckey)) { + throw key_error("CMalleablePubKey::GetVariant() : EC_KEY_generate_key failed"); + } + + EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED); + + int nSize = i2o_ECPublicKey(eckey, NULL); + if (!nSize) { + throw key_error("CMalleablePubKey::GetVariant() : i2o_ECPublicKey failed"); + } + + std::vector vchPubKey(nSize, 0); + unsigned char* pbegin_R = &vchPubKey[0]; + + if (i2o_ECPublicKey(eckey, &pbegin_R) != nSize) { + throw key_error("CMalleablePubKey::GetVariant() : i2o_ECPublicKey returned unexpected size"); + } + + // R = G*r + R = CPubKey(vchPubKey); + + // OpenSSL BIGNUM representation of r value + CBigNum bnr; + bnr = *(CBigNum*) EC_KEY_get0_private_key(eckey); + EC_KEY_free(eckey); + + CPoint point; + if (!point.setPubKey(pubKeyL)) { + throw key_error("CMalleablePubKey::GetVariant() : Unable to decode L value"); + } + + // Calculate L*r + point.ECMUL(bnr); + + std::vector vchLr; + if (!point.getBytes(vchLr)) { + throw key_error("CMalleablePubKey::GetVariant() : Unable to convert Lr value"); + } + + // Calculate Hash(L*r) and then get a BIGNUM representation of hash value. + CBigNum bnHash; + bnHash.setuint160(Hash160(vchLr)); + + CPoint pointH; + pointH.setPubKey(pubKeyH); + + CPoint P; + // Calculate P = Hash(L*r)*G + H + P.ECMULGEN(bnHash, pointH); + + if (P.IsInfinity()) { + throw key_error("CMalleablePubKey::GetVariant() : P is infinity"); + } + + std::vector vchResult; + P.getBytes(vchResult); + + vchPubKeyVariant = CPubKey(vchResult); +} + +std::string CMalleablePubKey::ToString() const +{ + CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION); + ssKey << *this; + std::vector vch(ssKey.begin(), ssKey.end()); + + return EncodeBase58Check(vch); +} + +bool CMalleablePubKey::SetString(const std::string& strMalleablePubKey) +{ + std::vector vchTemp; + if (!DecodeBase58Check(strMalleablePubKey, vchTemp)) { + throw key_error("CMalleablePubKey::SetString() : Provided key data seems corrupted."); + } + + CDataStream ssKey(vchTemp, SER_NETWORK, PROTOCOL_VERSION); + ssKey >> *this; + + return IsValid(); +} + +bool CMalleablePubKey::operator==(const CMalleablePubKey &b) +{ + return (nVersion == b.nVersion && + pubKeyL == b.pubKeyL && + pubKeyH == b.pubKeyH); +} + + +// CMalleableKey + +void CMalleableKey::Reset() +{ + vchSecretL.clear(); + vchSecretH.clear(); + + nVersion = 0; +} + +void CMalleableKey::MakeNewKeys() +{ + CKey L, H; + bool fCompressed = true; + + L.MakeNewKey(true); + H.MakeNewKey(true); + + vchSecretL = L.GetSecret(fCompressed); + vchSecretH = H.GetSecret(fCompressed); + + nVersion = CURRENT_VERSION; +} + +CMalleableKey::CMalleableKey() +{ + Reset(); +} + +CMalleableKey::CMalleableKey(const CMalleableKey &b) +{ + SetSecrets(b.vchSecretL, b.vchSecretH); +} + +CMalleableKey::CMalleableKey(const CSecret &L, const CSecret &H) +{ + SetSecrets(L, H); +} + +CMalleableKey& CMalleableKey::operator=(const CMalleableKey &b) +{ + SetSecrets(b.vchSecretL, b.vchSecretH); + + return (*this); +} + +CMalleableKey::~CMalleableKey() +{ +} + +bool CMalleableKey::IsNull() const +{ + return nVersion != CURRENT_VERSION; +} + +bool CMalleableKey::SetSecrets(const CSecret &pvchSecretL, const CSecret &pvchSecretH) +{ + Reset(); + CKey L, H; + + if (pvchSecretL.size() != 32 || !pvchSecretH.size() != 32 || !L.SetSecret(pvchSecretL, true) || !H.SetSecret(pvchSecretH, true)) + { + nVersion = 0; + return false; + } + + vchSecretL = pvchSecretL; + vchSecretH = pvchSecretH; + nVersion = CURRENT_VERSION; + + return true; +} + +void CMalleableKey::GetSecrets(CSecret &pvchSecretL, CSecret &pvchSecretH) const +{ + pvchSecretL = vchSecretL; + pvchSecretH = vchSecretH; +} + +CMalleablePubKey CMalleableKey::GetMalleablePubKey() const +{ + CKey L, H; + L.SetSecret(vchSecretL, true); + H.SetSecret(vchSecretH, true); + + std::vector vchPubKeyL = L.GetPubKey().Raw(); + std::vector vchPubKeyH = H.GetPubKey().Raw(); + + return CMalleablePubKey(vchPubKeyL, vchPubKeyH); +} + +// Check ownership +bool CMalleableKey::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant) const +{ + if (IsNull()) { + throw key_error("CMalleableKey::CheckKeyVariant() : Attempting to run on NULL key object."); + } + + if (!R.IsValid()) { + throw key_error("CMalleableKey::CheckKeyVariant() : R is invalid"); + } + + if (!vchPubKeyVariant.IsValid()) { + throw key_error("CMalleableKey::CheckKeyVariant() : public key variant is invalid"); + } + + CPoint point_R; + if (!point_R.setPubKey(R)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode R value"); + } + + CKey H; + H.SetSecret(vchSecretH, true); + std::vector vchPubKeyH = H.GetPubKey().Raw(); + + CPoint point_H; + if (!point_H.setPubKey(vchPubKeyH)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode H value"); + } + + CPoint point_P; + if (!point_P.setPubKey(vchPubKeyVariant)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode P value"); + } + + // Infinity points are senseless + if (point_P.IsInfinity()) { + throw key_error("CMalleableKey::CheckKeyVariant() : P is infinity"); + } + + CBigNum bnl; + bnl.setBytes(std::vector(vchSecretL.begin(), vchSecretL.end())); + + point_R.ECMUL(bnl); + + std::vector vchRl; + if (!point_R.getBytes(vchRl)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to convert Rl value"); + } + + // Calculate Hash(R*l) + CBigNum bnHash; + bnHash.setuint160(Hash160(vchRl)); + + CPoint point_Ps; + // Calculate Ps = Hash(L*r)*G + H + point_Ps.ECMULGEN(bnHash, point_H); + + // Infinity points are senseless + if (point_Ps.IsInfinity()) { + throw key_error("CMalleableKey::CheckKeyVariant() : Ps is infinity"); + } + + // Check ownership + if (point_Ps != point_P) { + return false; + } + + return true; +} + +// Check ownership and restore private key +bool CMalleableKey::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant, CKey &privKeyVariant) const +{ + if (IsNull()) { + throw key_error("CMalleableKey::CheckKeyVariant() : Attempting to run on NULL key object."); + } + + if (!R.IsValid()) { + throw key_error("CMalleableKey::CheckKeyVariant() : R is invalid"); + } + + if (!vchPubKeyVariant.IsValid()) { + throw key_error("CMalleableKey::CheckKeyVariant() : public key variant is invalid"); + } + + CPoint point_R; + if (!point_R.setPubKey(R)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode R value"); + } + + CKey H; + H.SetSecret(vchSecretH, true); + std::vector vchPubKeyH = H.GetPubKey().Raw(); + + CPoint point_H; + if (!point_H.setPubKey(vchPubKeyH)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode H value"); + } + + CPoint point_P; + if (!point_P.setPubKey(vchPubKeyVariant)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to decode P value"); + } + + // Infinity points are senseless + if (point_P.IsInfinity()) { + throw key_error("CMalleableKey::CheckKeyVariant() : P is infinity"); + } + + CBigNum bnl; + bnl.setBytes(std::vector(vchSecretL.begin(), vchSecretL.end())); + + point_R.ECMUL(bnl); + + std::vector vchRl; + if (!point_R.getBytes(vchRl)) { + throw key_error("CMalleableKey::CheckKeyVariant() : Unable to convert Rl value"); + } + + // Calculate Hash(R*l) + CBigNum bnHash; + bnHash.setuint160(Hash160(vchRl)); + + CPoint point_Ps; + // Calculate Ps = Hash(L*r)*G + H + point_Ps.ECMULGEN(bnHash, point_H); + + // Infinity points are senseless + if (point_Ps.IsInfinity()) { + throw key_error("CMalleableKey::CheckKeyVariant() : Ps is infinity"); + } + + // Check ownership + if (point_Ps != point_P) { + return false; + } + + // OpenSSL BIGNUM representation of the second private key from (l, h) pair + CBigNum bnh; + bnh.setBytes(std::vector(vchSecretH.begin(), vchSecretH.end())); + + // Calculate p = Hash(R*l) + h + CBigNum bnp = bnHash + bnh; + + std::vector vchp = bnp.getBytes(); + privKeyVariant.SetSecret(CSecret(vchp.begin(), vchp.end()), true); + + return true; +} + +std::string CMalleableKey::ToString() const +{ + CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION); + ssKey << *this; + std::vector vch(ssKey.begin(), ssKey.end()); + + return EncodeBase58Check(vch); +} + +bool CMalleableKey::SetString(const std::string& strMutableKey) +{ + std::vector vchTemp; + if (!DecodeBase58Check(strMutableKey, vchTemp)) { + throw key_error("CMalleableKey::SetString() : Provided key data seems corrupted."); + } + + CDataStream ssKey(vchTemp, SER_NETWORK, PROTOCOL_VERSION); + ssKey >> *this; + + return IsNull(); +} + +// CMalleableKeyView + +CMalleableKeyView::CMalleableKeyView(const CMalleableKey &b) +{ + if (b.vchSecretL.size() != 32) + throw key_error("CMalleableKeyView::CMalleableKeyView() : L size must be 32 bytes"); + + if (b.vchSecretH.size() != 32) + throw key_error("CMalleableKeyView::CMalleableKeyView() : L size must be 32 bytes"); + + vchSecretL = b.vchSecretL; + + CKey H; + H.SetSecret(b.vchSecretH, true); + + vchPubKeyH = H.GetPubKey().Raw(); + nVersion = b.nVersion; +} + +CMalleableKeyView::CMalleableKeyView(const CMalleableKeyView &b) +{ + vchSecretL = b.vchSecretL; + vchPubKeyH = b.vchPubKeyH; + nVersion = CURRENT_VERSION; +} + +CMalleableKeyView::CMalleableKeyView(const CSecret &L, const CPubKey &pvchPubKeyH) +{ + vchSecretL = L; + vchPubKeyH = pvchPubKeyH.Raw(); + nVersion = CURRENT_VERSION; +} + +CMalleableKeyView& CMalleableKeyView::operator=(const CMalleableKey &b) +{ + vchSecretL = b.vchSecretL; + + CKey H; + H.SetSecret(b.vchSecretH, true); + vchPubKeyH = H.GetPubKey().Raw(); + nVersion = b.nVersion; + + return (*this); +} + +CMalleableKeyView::~CMalleableKeyView() +{ +} + +CMalleablePubKey CMalleableKeyView::GetMalleablePubKey() const +{ + CKey keyL; + keyL.SetSecret(vchSecretL, true); + return CMalleablePubKey(keyL.GetPubKey(), vchPubKeyH); +} + +// Check ownership +bool CMalleableKeyView::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant) const +{ + if (!R.IsValid()) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : R is invalid"); + } + + if (!vchPubKeyVariant.IsValid()) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : public key variant is invalid"); + } + + CPoint point_R; + if (!point_R.setPubKey(R)) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode R value"); + } + + CPoint point_H; + if (!point_H.setPubKey(vchPubKeyH)) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode H value"); + } + + CPoint point_P; + if (!point_P.setPubKey(vchPubKeyVariant)) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to decode P value"); + } + + // Infinity points are senseless + if (point_P.IsInfinity()) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : P is infinity"); + } + + CBigNum bnl; + bnl.setBytes(std::vector(vchSecretL.begin(), vchSecretL.end())); + + point_R.ECMUL(bnl); + + std::vector vchRl; + if (!point_R.getBytes(vchRl)) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : Unable to convert Rl value"); + } + + // Calculate Hash(R*l) + CBigNum bnHash; + bnHash.setuint160(Hash160(vchRl)); + + CPoint point_Ps; + // Calculate Ps = Hash(L*r)*G + H + point_Ps.ECMULGEN(bnHash, point_H); + + // Infinity points are senseless + if (point_Ps.IsInfinity()) { + throw key_error("CMalleableKeyView::CheckKeyVariant() : Ps is infinity"); + } + + // Check ownership + if (point_Ps != point_P) { + return false; + } + + return true; +} + +std::string CMalleableKeyView::ToString() const +{ + CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION); + ssKey << *this; + std::vector vch(ssKey.begin(), ssKey.end()); + + return EncodeBase58Check(vch); +} + +bool CMalleableKeyView::SetString(const std::string& strMutableKey) +{ + std::vector vchTemp; + if (!DecodeBase58Check(strMutableKey, vchTemp)) { + throw key_error("CMalleableKeyView::SetString() : Provided key data seems corrupted."); + } + + CDataStream ssKey(vchTemp, SER_NETWORK, PROTOCOL_VERSION); + ssKey >> *this; + + return IsNull(); +} + +bool CMalleableKeyView::IsNull() const +{ + return nVersion != CURRENT_VERSION; +} + +//// Asymmetric encryption + +void CPubKey::EncryptData(const std::vector& data, std::vector& encrypted) +{ + CKey key; + key.SetPubKey(*this); + + key.EncryptData(data, encrypted); +} + +void CKey::EncryptData(const std::vector& data, std::vector& encrypted) +{ + ies_ctx_t *ctx; + char error[1024] = "Unknown error"; + cryptogram_t *cryptogram; + + ctx = create_context(pkey); + if (!EC_KEY_get0_public_key(ctx->user_key)) + throw key_error("Given EC key is not public key"); + + cryptogram = ecies_encrypt(ctx, (unsigned char*)&data[0], data.size(), error); + if (cryptogram == NULL) { + free(ctx); + ctx = NULL; + throw key_error(std::string("Error in encryption: %s") + error); + } + + encrypted.resize(cryptogram_data_sum_length(cryptogram)); + unsigned char *key_data = cryptogram_key_data(cryptogram); + memcpy(&encrypted[0], key_data, encrypted.size()); + cryptogram_free(cryptogram); + free(ctx); +} + +void CKey::DecryptData(const std::vector& encrypted, std::vector& data) +{ + ies_ctx_t *ctx; + char error[1024] = "Unknown error"; + cryptogram_t *cryptogram; + size_t length; + unsigned char *decrypted; + + ctx = create_context(pkey); + if (!EC_KEY_get0_private_key(ctx->user_key)) + throw key_error("Given EC key is not private key"); + + size_t key_length = ctx->stored_key_length; + size_t mac_length = EVP_MD_size(ctx->md); + cryptogram = cryptogram_alloc(key_length, mac_length, encrypted.size() - key_length - mac_length); + + memcpy(cryptogram_key_data(cryptogram), &encrypted[0], encrypted.size()); + + decrypted = ecies_decrypt(ctx, cryptogram, &length, error); + cryptogram_free(cryptogram); + free(ctx); + + if (decrypted == NULL) { + throw key_error(std::string("Error in decryption: %s") + error); + } + + data.resize(length); + memcpy(&data[0], decrypted, length); + free(decrypted); +} diff --git a/src/key.h b/src/key.h index e329cf0..a829834 100644 --- a/src/key.h +++ b/src/key.h @@ -13,6 +13,7 @@ #include "uint256.h" #include "hash.h" #include "bignum.h" +#include "ies.h" #include // for EC_KEY definition @@ -97,6 +98,9 @@ public: std::vector Raw() const { return vchPubKey; } + + // Encrypt data + void EncryptData(const std::vector& data, std::vector& encrypted); }; @@ -164,6 +168,156 @@ public: // Reserialize to DER static bool ReserealizeSignature(std::vector& vchSig); + + // Encrypt data + void EncryptData(const std::vector& data, std::vector& encrypted); + + // Decrypt data + void DecryptData(const std::vector& encrypted, std::vector& data); +}; + +class CPoint +{ +private: + EC_POINT *point; + EC_GROUP* group; + BN_CTX* ctx; + +public: + CPoint(); + bool operator!=(const CPoint &a); + ~CPoint(); + + // Initialize from octets stream + bool setBytes(const std::vector &vchBytes); + + // Initialize from pubkey + bool setPubKey(const CPubKey &vchPubKey); + + // Serialize to octets stream + bool getBytes(std::vector &vchBytes); + + // ECC multiplication by specified multiplier + bool ECMUL(const CBigNum &bnMultiplier); + + // Calculate G*m + q + bool ECMULGEN(const CBigNum &bnMultiplier, const CPoint &qPoint); + + bool IsInfinity() { return EC_POINT_is_at_infinity(group, point); } +}; + +class CMalleablePubKey +{ +private: + unsigned char nVersion; + CPubKey pubKeyL; + CPubKey pubKeyH; + friend class CMalleableKey; + + static const unsigned char CURRENT_VERSION = 1; + +public: + CMalleablePubKey() { nVersion = CMalleablePubKey::CURRENT_VERSION; } + CMalleablePubKey(const std::string& strMalleablePubKey) { SetString(strMalleablePubKey); } + CMalleablePubKey(const CPubKey &pubKeyInL, const CPubKey &pubKeyInH) : pubKeyL(pubKeyInL), pubKeyH(pubKeyInH) { nVersion = CMalleablePubKey::CURRENT_VERSION; } + CMalleablePubKey(const std::vector &pubKeyInL, const std::vector &pubKeyInH) : pubKeyL(pubKeyInL), pubKeyH(pubKeyInH) { nVersion = CMalleablePubKey::CURRENT_VERSION; } + + IMPLEMENT_SERIALIZE( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(pubKeyL); + READWRITE(pubKeyH); + ) + + bool IsValid() const { + return pubKeyL.IsValid() && pubKeyH.IsValid(); + } + + bool operator==(const CMalleablePubKey &b); + bool operator!=(const CMalleablePubKey &b) { return !(*this == b); } + + std::string ToString() const; + bool SetString(const std::string& strMalleablePubKey); + uint256 GetID() const; + + CPubKey& GetL() { return pubKeyL; } + CPubKey& GetH() { return pubKeyH; } + void GetVariant(CPubKey &R, CPubKey &vchPubKeyVariant); +}; + +class CMalleableKey +{ +private: + unsigned char nVersion; + CSecret vchSecretL; + CSecret vchSecretH; + + friend class CMalleableKeyView; + + static const unsigned char CURRENT_VERSION = 1; + +public: + CMalleableKey(); + CMalleableKey(const CMalleableKey &b); + CMalleableKey(const CSecret &L, const CSecret &H); + CMalleableKey& operator=(const CMalleableKey &b); + ~CMalleableKey(); + + IMPLEMENT_SERIALIZE( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vchSecretL); + READWRITE(vchSecretH); + ) + + std::string ToString() const; + bool SetString(const std::string& strMalleablePubKey); + + void Reset(); + void MakeNewKeys(); + bool IsNull() const; + bool SetSecrets(const CSecret &pvchSecretL, const CSecret &pvchSecretH); + void GetSecrets(CSecret &pvchSecretL, CSecret &pvchSecretH) const; + + CMalleablePubKey GetMalleablePubKey() const; + bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant) const; + bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant, CKey &privKeyVariant) const; +}; + +class CMalleableKeyView +{ +private: + unsigned char nVersion; + CSecret vchSecretL; + CPubKey vchPubKeyH; + + static const unsigned char CURRENT_VERSION = 1; + +public: + CMalleableKeyView() { nVersion = 0; }; + CMalleableKeyView(const CMalleableKey &b); + CMalleableKeyView(const CSecret &L, const CPubKey &pvchPubKeyH); + + CMalleableKeyView(const CMalleableKeyView &b); + CMalleableKeyView& operator=(const CMalleableKey &b); + ~CMalleableKeyView(); + + + IMPLEMENT_SERIALIZE( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vchSecretL); + READWRITE(vchPubKeyH); + ) + + bool IsNull() const; + std::string ToString() const; + bool SetString(const std::string& strMalleablePubKey); + + CMalleablePubKey GetMalleablePubKey() const; + bool CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubKeyVariant) const; + + bool operator <(const CMalleableKeyView& kv) const { return vchPubKeyH.GetID() < kv.vchPubKeyH.GetID(); } }; #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index ef61748..fdba291 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -102,6 +102,19 @@ bool CBasicKeyStore::HaveWatchOnly() const return (!setWatchOnly.empty()); } +CCryptoKeyStore::CCryptoKeyStore() : fUseCrypto(false) +{ + std::string strMalleableKey = GetArg("-masterkey", ""); + CMalleableKey malleableKey; + if (strMalleableKey != "") + malleableKey.SetString(strMalleableKey); + else + malleableKey.MakeNewKeys(); + + const CMalleableKeyView& keyView(malleableKey); + mapMalleableKeys[keyView] = malleableKey; +} + bool CCryptoKeyStore::SetCrypted() { { @@ -154,6 +167,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) break; return false; } + vMasterKey = vMasterKeyIn; } NotifyStatusChanged(this); diff --git a/src/keystore.h b/src/keystore.h index 96285fb..7d49735 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -64,17 +64,24 @@ public: vchSecret = key.GetSecret(fCompressed); return true; } + + virtual bool CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R) const =0; + virtual bool CreatePrivKey(const CPubKey &pubKeyVariant, const CPubKey &R, CKey &privKey) const =0; + virtual void ListMalleablePubKeys(std::list &malleablePubKeyList) const =0; }; typedef std::map > KeyMap; typedef std::map ScriptMap; typedef std::set WatchOnlySet; +typedef std::map MalleableKeyMap; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore { protected: KeyMap mapKeys; + MalleableKeyMap mapMalleableKeys; + ScriptMap mapScripts; WatchOnlySet setWatchOnly; @@ -124,6 +131,43 @@ public: virtual bool RemoveWatchOnly(const CScript &dest); virtual bool HaveWatchOnly(const CScript &dest) const; virtual bool HaveWatchOnly() const; + + bool CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R) const + { + { + LOCK(cs_KeyStore); + for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) + { + if (mi->first.CheckKeyVariant(R, pubKeyVariant)) + return true; + } + } + return false; + } + + bool CreatePrivKey(const CPubKey &pubKeyVariant, const CPubKey &R, CKey &privKey) const + { + { + LOCK(cs_KeyStore); + for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) + { + if (mi->second.CheckKeyVariant(R, pubKeyVariant, privKey)) + return true; + } + } + return false; + } + + void ListMalleablePubKeys(std::list &malleablePubKeyList) const + { + malleablePubKeyList.clear(); + + { + LOCK(cs_KeyStore); + for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) + malleablePubKeyList.push_back(mi->first.GetMalleablePubKey()); + } + } }; typedef std::map > > CryptedKeyMap; @@ -152,9 +196,7 @@ protected: bool Unlock(const CKeyingMaterial& vMasterKeyIn); public: - CCryptoKeyStore() : fUseCrypto(false) - { - } + CCryptoKeyStore(); bool IsCrypted() const { diff --git a/src/makefile.bsd b/src/makefile.bsd index 786d5ef..a22ce33 100644 --- a/src/makefile.bsd +++ b/src/makefile.bsd @@ -131,7 +131,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 080af03..e255112 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -98,7 +98,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind.exe diff --git a/src/makefile.mingw b/src/makefile.mingw index feacdef..e0bd3e0 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -88,7 +88,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind.exe diff --git a/src/makefile.osx b/src/makefile.osx index 388c5be..96cf542 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -95,7 +95,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o ifneq (${USE_IPV6}, -) DEFS += -DUSE_IPV6=$(USE_IPV6) diff --git a/src/makefile.unix b/src/makefile.unix index 6ec04ce..a37bf0e 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -132,7 +132,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind diff --git a/src/protocol.h b/src/protocol.h index ecc2e08..b4ebac6 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -19,7 +19,7 @@ extern bool fTestNet; static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) { - return testnet ? 17777 : 7777; + return static_cast(testnet ? 17777 : 7777); } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 4fa6f67..5bec8b4 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -12,6 +12,8 @@ #include "main.h" #include "net.h" #include "wallet.h" +#include "script.h" +#include "util.h" using namespace std; using namespace boost; @@ -35,13 +37,20 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH return; } - out.push_back(Pair("reqSigs", nRequired)); - out.push_back(Pair("type", GetTxnOutputType(type))); + if (type != TX_NULL_DATA) + { + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); - Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - out.push_back(Pair("addresses", a)); + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); + } + else + { + out.push_back(Pair("type", GetTxnOutputType(type))); + } } void TxToJSON(const CTransaction& tx, const uint256& hashBlock, Object& entry) @@ -226,12 +235,13 @@ Value listunspent(const Array& params, bool fHelp) Value createrawtransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() != 2) + if (fHelp || params.size() > 3 || params.size() < 2) throw runtime_error( - "createrawtransaction '[{\"txid\":txid,\"vout\":n},...]' '{address:amount,...}'\n" + "createrawtransaction <'[{\"txid\":txid,\"vout\":n},...]'> <'{address:amount,...}'> [hex data]\n" "Create a transaction spending given inputs\n" "(array of objects containing transaction id and output number),\n" - "sending to given address(es).\n" + "sending to given address(es),\n" + "optional data to add into data-carrying output.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network."); @@ -284,6 +294,15 @@ Value createrawtransaction(const Array& params, bool fHelp) rawTx.vout.push_back(out); } + if (params.size() == 3) + { + // Data carrying output + CScript scriptPubKey; + scriptPubKey << OP_RETURN << ParseHex(params[2].get_str()); + CTxOut out(0, scriptPubKey); + rawTx.vout.push_back(out); + } + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << rawTx; return HexStr(ss.begin(), ss.end()); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 60ef03e..6a7a3b1 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1833,25 +1833,125 @@ Value resendtx(const Array& params, bool fHelp) return Value::null; } -// ppcoin: make a public-private key pair +// Make a public-private key pair Value makekeypair(const Array& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() > 0) throw runtime_error( - "makekeypair [prefix]\n" - "Make a public/private key pair.\n" - "[prefix] is optional preferred prefix for the public key.\n"); + "makekeypair\n" + "Make a public/private key pair.\n"); string strPrefix = ""; if (params.size() > 0) strPrefix = params[0].get_str(); - + CKey key; - key.MakeNewKey(false); + key.MakeNewKey(true); CPrivKey vchPrivKey = key.GetPrivKey(); Object result; result.push_back(Pair("PrivateKey", HexStr(vchPrivKey.begin(), vchPrivKey.end()))); + + bool fCompressed; + CSecret vchSecret = key.GetSecret(fCompressed); + result.push_back(Pair("Secret", HexStr(vchSecret.begin(), vchSecret.end()))); result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw()))); return result; } + +Value newmalleablekey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "newmalleablekey\n" + "Make a malleable public/private key pair.\n"); + + CMalleableKey malleableKey; + malleableKey.MakeNewKeys(); + CMalleablePubKey malleablePubKey = malleableKey.GetMalleablePubKey(); + + CDataStream ssPublicBytes(SER_NETWORK, PROTOCOL_VERSION); + ssPublicBytes << malleablePubKey; + + Object result; + result.push_back(Pair("PrivatePair", malleableKey.ToString())); + result.push_back(Pair("PublicPair", malleablePubKey.ToString())); + result.push_back(Pair("PublicBytes", HexStr(ssPublicBytes.begin(), ssPublicBytes.end()))); + + return result; +} + +Value adjustmalleablekey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "adjustmalleablekey \n" + "Calculate new private key using provided malleable key, public key and R data.\n"); + + CMalleableKey malleableKey; + malleableKey.SetString(params[0].get_str()); + + CKey privKeyVariant; + CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str())); + + CPubKey R(ParseHex(params[2].get_str())); + + if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) { + throw runtime_error("Unable to calculate the private key"); + } + + Object result; + bool fCompressed; + CSecret vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed); + + result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString())); + + return result; +} + +Value adjustmalleablepubkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2 || params.size() == 0) + throw runtime_error( + "adjustmalleablepubkey \n" + "Calculate new public key using provided malleable public key data.\n"); + + string pubKeyPair = params[0].get_str(); + CMalleablePubKey malleablePubKey; + + if (pubKeyPair.size() == 138) { + CDataStream ssPublicBytes(ParseHex(pubKeyPair), SER_NETWORK, PROTOCOL_VERSION); + ssPublicBytes >> malleablePubKey; + } else + malleablePubKey.SetString(pubKeyPair); + + CPubKey R, vchPubKeyVariant; + malleablePubKey.GetVariant(R, vchPubKeyVariant); + + Object result; + result.push_back(Pair("R", HexStr(R.Raw()))); + result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw()))); + result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString())); + + + return result; +} + +Value listmalleablepubkeys(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listmalleablepubkeys\n" + "Get list of malleable public keys.\n"); + + std::list keyList; + pwalletMain->ListMalleablePubKeys(keyList); + + Array result; + BOOST_FOREACH(const CMalleablePubKey &key, keyList) + { + result.push_back(key.ToString()); + } + + return result; +} diff --git a/src/script.cpp b/src/script.cpp index 8057bdb..26b7c94 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -99,6 +99,7 @@ const char* GetTxnOutputType(txnouttype t) { case TX_NONSTANDARD: return "nonstandard"; case TX_PUBKEY: return "pubkey"; + case TX_PUBKEY_DROP: return "pubkeydrop"; case TX_PUBKEYHASH: return "pubkeyhash"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; @@ -1194,6 +1195,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector SMALLDATA_SWITCH_TIME) + { + // Malleable pubkey tx hack, sender provides generated pubkey combined with R parameter. The R parameter is dropped before checking a signature. + mTemplates.insert(make_pair(TX_PUBKEY_DROP, CScript() << OP_PUBKEY << OP_PUBKEY << OP_DROP << OP_CHECKSIG)); + } + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); @@ -1288,8 +1295,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector 80) + // small pushdata, <= 1024 bytes + if (vch1.size() > (GetTime() > SMALLDATA_SWITCH_TIME ? 1024 : 80)) break; } else if (opcode1 != opcode2 || vch1 != vch2) @@ -1306,7 +1313,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +bool SignR(const CPubKey& pubKey, const CPubKey& R, const CKeyStore& keystore, const uint256& hash, int nHashType, CScript& scriptSigRet) +{ + CKey key; + if (!keystore.CreatePrivKey(pubKey, R, key)) + return false; + + vector vchSig; + if (!key.Sign(hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} + +bool SignN(const vector& multisigdata, const CKeyStore& keystore, const uint256& hash, int nHashType, CScript& scriptSigRet) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -1341,7 +1363,7 @@ bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint2 // unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. // Returns false if scriptPubKey could not be completely satisfied. // -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, const uint256& hash, int nHashType, CScript& scriptSigRet, txnouttype& whichTypeRet) { scriptSigRet.clear(); @@ -1359,6 +1381,12 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEY_DROP: + { + CPubKey key = CPubKey(vSolutions[0]); + CPubKey R = CPubKey(vSolutions[1]); + return SignR(key, R, keystore, hash, nHashType, scriptSigRet); + } case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) @@ -1389,6 +1417,7 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector& values) return result; } -static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const vector& vSolutions, vector& sigs1, vector& sigs2) { @@ -1755,7 +1797,7 @@ static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, u return result; } -static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const txnouttype txType, const vector& vSolutions, vector& sigs1, vector& sigs2) { @@ -1768,6 +1810,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, return PushAll(sigs1); return PushAll(sigs2); case TX_PUBKEY: + case TX_PUBKEY_DROP: case TX_PUBKEYHASH: // Signatures are bigger than placeholders or empty scripts: if (sigs1.empty() || sigs1[0].empty()) @@ -1800,7 +1843,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, return CScript(); } -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2) { txnouttype txType; diff --git a/src/script.h b/src/script.h index 745f848..4896630 100644 --- a/src/script.h +++ b/src/script.h @@ -76,6 +76,7 @@ enum txnouttype TX_NONSTANDARD, // 'standard' transaction types: TX_PUBKEY, + TX_PUBKEY_DROP, TX_PUBKEYHASH, TX_SCRIPTHASH, TX_MULTISIG, @@ -617,6 +618,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, // combine them intelligently and return the result. -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); #endif diff --git a/src/timestamps.h b/src/timestamps.h index 0d876bd..0da5341 100644 --- a/src/timestamps.h +++ b/src/timestamps.h @@ -3,5 +3,6 @@ static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Saturday, 20-Jul-2013 00:00:00 UTC static const unsigned int COINBASE_SIGOPS_SWITCH_TIME = 1447977600; // Friday, 20-Nov-15 00:00:00 UTC +static const unsigned int SMALLDATA_SWITCH_TIME = 1458432000; // Sunday, 20-Mar-16 00:00:00 UTC #endif