Merge pull request #272 from svost/V668
author0xDEADFACE <masmfan@gmail.com>
Sat, 13 Feb 2016 22:52:28 +0000 (01:52 +0300)
committer0xDEADFACE <masmfan@gmail.com>
Sat, 13 Feb 2016 22:52:28 +0000 (01:52 +0300)
Fix V668 PVS Studio - deal with pointer and NULL

23 files changed:
doc/MalleableKeys.txt [new file with mode: 0644]
novacoin-qt.pro
src/bitcoinrpc.cpp
src/bitcoinrpc.h
src/cryptogram.cpp [new file with mode: 0644]
src/ecies.cpp [new file with mode: 0644]
src/ies.h [new file with mode: 0644]
src/kernelrecord.cpp
src/key.cpp
src/key.h
src/keystore.cpp
src/keystore.h
src/makefile.bsd
src/makefile.linux-mingw
src/makefile.mingw
src/makefile.osx
src/makefile.unix
src/protocol.h
src/rpcrawtransaction.cpp
src/rpcwallet.cpp
src/script.cpp
src/script.h
src/timestamps.h

diff --git a/doc/MalleableKeys.txt b/doc/MalleableKeys.txt
new file mode 100644 (file)
index 0000000..357ef95
--- /dev/null
@@ -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
index a0852e5..1759e98 100644 (file)
@@ -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
index b10cee7..92042d0 100644 (file)
@@ -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},
 };
 
index dcf0863..226007b 100644 (file)
@@ -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 (file)
index 0000000..c107b44
--- /dev/null
@@ -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 (file)
index 0000000..215d390
--- /dev/null
@@ -0,0 +1,568 @@
+/**
+ * @file /cryptron/ecies.c
+ *
+ * @brief ECIES encryption/decryption functions.
+ *
+ * $Author: Ladar Levison $
+ * $Website: http://lavabit.com $
+ *
+ */
+
+#include "ies.h"
+#include <iostream>
+#include <vector>
+#include <openssl/ecdh.h>
+
+#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 (file)
index 0000000..bf69a82
--- /dev/null
+++ b/src/ies.h
@@ -0,0 +1,44 @@
+/**
+ * reference: ecies.h
+ */
+
+#ifndef _IES_H_
+#define _IES_H_
+
+#include <openssl/ssl.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+
+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_ */
index a9a2e8e..9c61d06 100644 (file)
@@ -47,7 +47,7 @@ vector<KernelRecord> 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<double>(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<double>(2),32) * difficulty);
 }
 
index e95fb17..dafffcf 100644 (file)
@@ -6,8 +6,12 @@
 
 #include <openssl/ecdsa.h>
 #include <openssl/obj_mac.h>
+#include <openssl/ssl.h>
+#include <openssl/ecdh.h>
 
 #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<unsigned char> &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<unsigned char> &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<unsigned char> 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<unsigned char> 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<unsigned char> vchResult;
+    P.getBytes(vchResult);
+
+    vchPubKeyVariant = CPubKey(vchResult);
+}
+
+std::string CMalleablePubKey::ToString() const
+{
+    CDataStream ssKey(SER_NETWORK, PROTOCOL_VERSION);
+    ssKey << *this;
+    std::vector<unsigned char> vch(ssKey.begin(), ssKey.end());
+
+    return EncodeBase58Check(vch);
+}
+
+bool CMalleablePubKey::SetString(const std::string& strMalleablePubKey)
+{
+    std::vector<unsigned char> 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<unsigned char> vchPubKeyL = L.GetPubKey().Raw();
+    std::vector<unsigned char> 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<unsigned char> 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<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> 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<unsigned char> 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<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> 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<unsigned char>(vchSecretH.begin(), vchSecretH.end()));
+
+    // Calculate p = Hash(R*l) + h
+    CBigNum bnp = bnHash + bnh;
+
+    std::vector<unsigned char> 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<unsigned char> vch(ssKey.begin(), ssKey.end());
+
+    return EncodeBase58Check(vch);
+}
+
+bool CMalleableKey::SetString(const std::string& strMutableKey)
+{
+    std::vector<unsigned char> 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<unsigned char>(vchSecretL.begin(), vchSecretL.end()));
+
+    point_R.ECMUL(bnl);
+
+    std::vector<unsigned char> 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<unsigned char> vch(ssKey.begin(), ssKey.end());
+
+    return EncodeBase58Check(vch);
+}
+
+bool CMalleableKeyView::SetString(const std::string& strMutableKey)
+{
+    std::vector<unsigned char> 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<unsigned char>& data, std::vector<unsigned char>& encrypted)
+{
+    CKey key;
+    key.SetPubKey(*this);
+
+    key.EncryptData(data, encrypted);
+}
+
+void CKey::EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& 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<unsigned char>& encrypted, std::vector<unsigned char>& 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);
+}
index e329cf0..a829834 100644 (file)
--- 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 <openssl/ec.h> // for EC_KEY definition
 
@@ -97,6 +98,9 @@ public:
     std::vector<unsigned char> Raw() const {
         return vchPubKey;
     }
+
+    // Encrypt data
+    void EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted);
 };
 
 
@@ -164,6 +168,156 @@ public:
 
     // Reserialize to DER
     static bool ReserealizeSignature(std::vector<unsigned char>& vchSig);
+
+    // Encrypt data
+    void EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted);
+
+    // Decrypt data
+    void DecryptData(const std::vector<unsigned char>& encrypted, std::vector<unsigned char>& 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<unsigned char> &vchBytes);
+
+    // Initialize from pubkey
+    bool setPubKey(const CPubKey &vchPubKey);
+
+    // Serialize to octets stream
+    bool getBytes(std::vector<unsigned char> &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<unsigned char> &pubKeyInL, const std::vector<unsigned char> &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
index ef61748..fdba291 100644 (file)
@@ -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);
index 96285fb..7d49735 100644 (file)
@@ -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<CMalleablePubKey> &malleablePubKeyList) const =0;
 };
 
 typedef std::map<CKeyID, std::pair<CSecret, bool> > KeyMap;
 typedef std::map<CScriptID, CScript > ScriptMap;
 typedef std::set<CScript> WatchOnlySet;
+typedef std::map<CMalleableKeyView, CMalleableKey> 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<CMalleablePubKey> &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<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
@@ -152,9 +196,7 @@ protected:
     bool Unlock(const CKeyingMaterial& vMasterKeyIn);
 
 public:
-    CCryptoKeyStore() : fUseCrypto(false)
-    {
-    }
+    CCryptoKeyStore();
 
     bool IsCrypted() const
     {
index 786d5ef..a22ce33 100644 (file)
@@ -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
 
index 080af03..e255112 100644 (file)
@@ -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
 
index feacdef..e0bd3e0 100644 (file)
@@ -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
 
index 388c5be..96cf542 100644 (file)
@@ -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)
index 6ec04ce..a37bf0e 100644 (file)
@@ -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
 
index ecc2e08..b4ebac6 100644 (file)
@@ -19,7 +19,7 @@
 extern bool fTestNet;
 static inline unsigned short GetDefaultPort(const bool testnet = fTestNet)
 {
-    return testnet ? 17777 : 7777;
+    return static_cast<unsigned short>(testnet ? 17777 : 7777);
 }
 
 
index 4fa6f67..5bec8b4 100644 (file)
@@ -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());
index 60ef03e..6a7a3b1 100644 (file)
@@ -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<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
+
+    bool fCompressed;
+    CSecret vchSecret = key.GetSecret(fCompressed);
+    result.push_back(Pair("Secret", HexStr<CSecret::iterator>(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 <Malleable key data> <Public key variant data> <R data>\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 <Malleable public key data>\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<CMalleablePubKey> keyList;
+    pwalletMain->ListMalleablePubKeys(keyList);
+
+    Array result;
+    BOOST_FOREACH(const CMalleablePubKey &key, keyList)
+    {
+        result.push_back(key.ToString());
+    }
+
+    return result;
+}
index 8057bdb..26b7c94 100644 (file)
@@ -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<vector<unsi
         // Standard tx, sender provides pubkey, receiver adds signature
         mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
 
+        if (GetTime() > 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<vector<unsi
             }
             else if (opcode2 == OP_SMALLDATA)
             {
-                // small pushdata, <= 80 bytes
-                if (vch1.size() > 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<vector<unsi
 }
 
 
-bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
+bool Sign1(const CKeyID& address, const CKeyStore& keystore, const uint256& hash, int nHashType, CScript& scriptSigRet)
 {
     CKey key;
     if (!keystore.GetKey(address, key))
@@ -1321,7 +1328,22 @@ bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int n
     return true;
 }
 
-bool SignN(const vector<valtype>& 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<unsigned char> vchSig;
+    if (!key.Sign(hash, vchSig))
+        return false;
+    vchSig.push_back((unsigned char)nHashType);
+    scriptSigRet << vchSig;
+
+    return true;
+}
+
+bool SignN(const vector<valtype>& 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<valtype>& 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<std::vector<unsigned c
     case TX_NULL_DATA:
         return 1;
     case TX_PUBKEY:
+    case TX_PUBKEY_DROP:
         return 1;
     case TX_PUBKEYHASH:
         return 2;
@@ -1475,6 +1504,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
         if (keystore.HaveKey(keyID))
             return MINE_SPENDABLE;
         break;
+    case TX_PUBKEY_DROP:
+        {
+            CPubKey key = CPubKey(vSolutions[0]);
+            if (keystore.HaveKey(key.GetID()))
+                return MINE_SPENDABLE;
+            else
+            {
+                CPubKey R = CPubKey(vSolutions[1]);
+                if (keystore.CheckOwnership(key, R))
+                    return MINE_SPENDABLE;
+            }
+        }
+        break;
     case TX_PUBKEYHASH:
         keyID = CKeyID(uint160(vSolutions[0]));
         if (keystore.HaveKey(keyID))
@@ -1517,7 +1559,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
     if (!Solver(scriptPubKey, whichType, vSolutions))
         return false;
 
-    if (whichType == TX_PUBKEY)
+    if (whichType == TX_PUBKEY || whichType == TX_PUBKEY_DROP)
     {
         addressRet = CPubKey(vSolutions[0]).GetID();
         return true;
@@ -1700,7 +1742,7 @@ static CScript PushAll(const vector<valtype>& 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<valtype>& vSolutions,
                                vector<valtype>& sigs1, vector<valtype>& 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<valtype>& vSolutions,
                                  vector<valtype>& sigs1, vector<valtype>& 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;
index 745f848..4896630 100644 (file)
@@ -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
index 0d876bd..0da5341 100644 (file)
@@ -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