Malleable keys: Crypted R parameter support.
author0xDEADFACE <masmfan@gmail.com>
Thu, 11 Feb 2016 22:22:54 +0000 (14:22 -0800)
committer0xDEADFACE <masmfan@gmail.com>
Thu, 11 Feb 2016 22:25:36 +0000 (14:25 -0800)
13 files changed:
doc/MalleableKeysCryptedR.txt [new file with mode: 0644]
novacoin-qt.pro
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/key.cpp
src/key.h
src/makefile.bsd
src/makefile.linux-mingw
src/makefile.mingw
src/makefile.osx
src/makefile.unix
src/rpcwallet.cpp

diff --git a/doc/MalleableKeysCryptedR.txt b/doc/MalleableKeysCryptedR.txt
new file mode 100644 (file)
index 0000000..9631fec
--- /dev/null
@@ -0,0 +1,30 @@
+Generate key pairs:
+
+> newmalleablekey
+{
+"PrivatePair" : "DgiohBCDx3N528wVLC3DZzAeAk3dnaSS7zt7dL3HBpNh4MAGk2xHRVxHQNNGg6eX3MmAV8bPLPMfRJ2m14wMjagpjpCsV33A",
+"PublicPair" : "5GRZdncReLgGZL7pjFQCpWMLzRpb4FxfYEGdaQeMSRZdZZWFmDR7qSuQHNxXX1UcAkdPSGiqdTKWfnayCX2vJbf2SgfoqdvC5mP",
+"PublicBytes" : "01210390db9818fc073642977989afb9e8e4c47adaf8d940a8a0562a0d7230bdbf8af1210326cf4bcaccb70d418dab3304d7b8eddf00c900ff8ce22eaa639214c40e937220"
+}
+
+Create public key variant:
+
+> adjustmalleablepubkey 5GRZdncReLgGZL7pjFQCpWMLzRpb4FxfYEGdaQeMSRZdZZWFmDR7qSuQHNxXX1UcAkdPSGiqdTKWfnayCX2vJbf2SgfoqdvC5mP
+{
+"R" : "020b2e43c4c5984d02c564a18f0efb64cb9d3617ff3154699d2ed4718d0512365d",
+"Rcrypted" : "03ffdb5faf6c54db89e14b11b92f28dfda1f65e09001276cc38491b69b94e85b1c4ee0d6ff62db2aaa5669f884afcab7b6690ea3cc97c5b4a2eba8960bcbf089c53dfb92225b4dfde0658419acc56976477dac66343a6621dea05889badcc68b0ed019edde",
+"PubkeyVariant" : "02f956d11ece637741bc96f441209dc36c78aa3f0320cc22433ae053a1d27a2a25",
+"KeyVariantID" : "4NA1e7f5qD68ApK8vbaX7VVjfAitSLbyNu"
+}
+
+Now, the both crypted and uncrypted R parameter should produce identical private keys:
+
+> adjustmalleablekey DgiohBCDx3N528wVLC3DZzAeAk3dnaSS7zt7dL3HBpNh4MAGk2xHRVxHQNNGg6eX3MmAV8bPLPMfRJ2m14wMjagpjpCsV33A 02f956d11ece637741bc96f441209dc36c78aa3f0320cc22433ae053a1d27a2a25 020b2e43c4c5984d02c564a18f0efb64cb9d3617ff3154699d2ed4718d0512365d
+{
+"PrivateKey" : "MBhrGekLruK6GsNTvWq9gJDtJ7EAGH2Rixv81ZwqbbGBah1Cz5jV"
+}
+
+> adjustmalleablekey DgiohBCDx3N528wVLC3DZzAeAk3dnaSS7zt7dL3HBpNh4MAGk2xHRVxHQNNGg6eX3MmAV8bPLPMfRJ2m14wMjagpjpCsV33A 02f956d11ece637741bc96f441209dc36c78aa3f0320cc22433ae053a1d27a2a25 03ffdb5faf6c54db89e14b11b92f28dfda1f65e09001276cc38491b69b94e85b1c4ee0d6ff62db2aaa5669f884afcab7b6690ea3cc97c5b4a2eba8960bcbf089c53dfb92225b4dfde0658419acc56976477dac66343a6621dea05889badcc68b0ed019edde
+{
+"PrivateKey" : "MBhrGekLruK6GsNTvWq9gJDtJ7EAGH2Rixv81ZwqbbGBah1Cz5jV"
+}
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
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 afe63cb..eb4f5c4 100644 (file)
@@ -7,9 +7,11 @@
 #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)
@@ -1095,16 +1097,65 @@ bool CMalleableKeyView::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubK
 
 //// Asymmetric encryption
 
-bool CPubKey::EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted)
+void CPubKey::EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted)
 {
-    // TODO
+    CKey key;
+    key.SetPubKey(*this);
 
-    return true;
+    key.EncryptData(data, encrypted);
 }
 
-bool CKey::DecryptData(const std::vector<unsigned char>& encrypted, std::vector<unsigned char>& data)
+void CKey::EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted)
 {
-    // TODO
+    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);
+    }
 
-    return true;
+    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 a3b23fe..f8404db 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
 
@@ -99,7 +100,7 @@ public:
     }
 
     // Encrypt data
-    bool EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted);
+    void EncryptData(const std::vector<unsigned char>& data, std::vector<unsigned char>& encrypted);
 };
 
 
@@ -168,8 +169,11 @@ 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
-    bool DecryptData(const std::vector<unsigned char>& encrypted, std::vector<unsigned char>& data);
+    void DecryptData(const std::vector<unsigned char>& encrypted, std::vector<unsigned char>& data);
 };
 
 class CPoint
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 22beb60..c1be536 100644 (file)
@@ -1893,9 +1893,26 @@ Value adjustmalleablekey(const Array& params, bool fHelp)
 
     CKey privKeyVariant;
     CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
-    CPubKey vchR = CPubKey(ParseHex(params[2].get_str()));
 
-    if (!malleableKey.CheckKeyVariant(vchR,vchPubKeyVariant, privKeyVariant)) {
+    std::vector<unsigned char> vchR = ParseHex(params[2].get_str());
+
+    CPubKey R;
+    if (vchR.size() == 33)
+        R = CPubKey(vchR);
+    else {
+        // R is encrypted
+        CSecret pvchSecretL, pvchSecretH;
+
+        malleableKey.GetSecrets(pvchSecretL, pvchSecretH);
+        CKey key;
+        key.SetSecret(pvchSecretL);
+
+        std::vector<unsigned char> vchDecryptedR;
+        key.DecryptData(vchR, vchDecryptedR);
+        R = CPubKey(vchDecryptedR);
+    }
+
+    if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
         throw runtime_error("Unable to calculate the private key");
     }
 
@@ -1927,10 +1944,15 @@ Value adjustmalleablepubkey(const Array& params, bool fHelp)
     CPubKey R, vchPubKeyVariant;
     malleablePubKey.GetVariant(R, vchPubKeyVariant);
 
+    std::vector<unsigned char> encryptedR;
+    malleablePubKey.GetL().EncryptData(R.Raw(), encryptedR);
+
     Object result;
     result.push_back(Pair("R", HexStr(R.Raw())));
+    result.push_back(Pair("Rcrypted", HexStr(encryptedR)));
     result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw())));
     result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
 
+
     return result;
 }