From b8dd44c350c3fb0b958cea2baba8aab268f44cf2 Mon Sep 17 00:00:00 2001 From: 0xDEADFACE Date: Thu, 11 Feb 2016 14:22:54 -0800 Subject: [PATCH] Malleable keys: Crypted R parameter support. --- doc/MalleableKeysCryptedR.txt | 30 +++ novacoin-qt.pro | 7 +- src/cryptogram.cpp | 65 +++++ src/ecies.cpp | 568 +++++++++++++++++++++++++++++++++++++++++ src/ies.h | 44 ++++ src/key.cpp | 63 ++++- src/key.h | 8 +- src/makefile.bsd | 4 +- src/makefile.linux-mingw | 4 +- src/makefile.mingw | 4 +- src/makefile.osx | 4 +- src/makefile.unix | 4 +- src/rpcwallet.cpp | 26 ++- 13 files changed, 814 insertions(+), 17 deletions(-) create mode 100644 doc/MalleableKeysCryptedR.txt create mode 100644 src/cryptogram.cpp create mode 100644 src/ecies.cpp create mode 100644 src/ies.h diff --git a/doc/MalleableKeysCryptedR.txt b/doc/MalleableKeysCryptedR.txt new file mode 100644 index 0000000..9631fec --- /dev/null +++ b/doc/MalleableKeysCryptedR.txt @@ -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" +} diff --git a/novacoin-qt.pro b/novacoin-qt.pro index a0852e5..1759e98 100644 --- a/novacoin-qt.pro +++ b/novacoin-qt.pro @@ -259,7 +259,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/multisiginputentry.h \ src/qt/multisigdialog.h \ src/qt/secondauthdialog.h \ - src/qt/qrcodedialog.h + src/qt/qrcodedialog.h \ + src/ies.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/intro.cpp \ @@ -338,7 +339,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/multisigdialog.cpp \ src/qt/secondauthdialog.cpp \ src/qt/qrcodedialog.cpp \ - src/base58.cpp + src/base58.cpp \ + src/cryptogram.cpp \ + src/ecies.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/cryptogram.cpp b/src/cryptogram.cpp new file mode 100644 index 0000000..c107b44 --- /dev/null +++ b/src/cryptogram.cpp @@ -0,0 +1,65 @@ +/** + * @file /cryptron/secure.c + * + * @brief Functions for handling the secure data type. + * + * $Author: Ladar Levison $ + * $Website: http://lavabit.com $ + * + */ + +#include "ies.h" +#define HEADSIZE (sizeof(cryptogram_head_t)) + +size_t cryptogram_key_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.key; +} + +size_t cryptogram_mac_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.mac; +} + +size_t cryptogram_body_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return head->length.body; +} + +size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (head->length.key + head->length.mac + head->length.body); +} + +size_t cryptogram_total_length(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return HEADSIZE + (head->length.key + head->length.mac + head->length.body); +} + +unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram) { + return (unsigned char *)cryptogram + HEADSIZE; +} + +unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (unsigned char *)cryptogram + (HEADSIZE + head->length.key + head->length.body); +} + +unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram) { + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + return (unsigned char *)cryptogram + (HEADSIZE + head->length.key); +} + +cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body) { + cryptogram_t *cryptogram = (cryptogram_t *) malloc(HEADSIZE + key + mac + body); + cryptogram_head_t *head = (cryptogram_head_t *)cryptogram; + head->length.key = key; + head->length.mac = mac; + head->length.body = body; + return cryptogram; +} + +void cryptogram_free(cryptogram_t *cryptogram) { + free(cryptogram); + return; +} diff --git a/src/ecies.cpp b/src/ecies.cpp new file mode 100644 index 0000000..215d390 --- /dev/null +++ b/src/ecies.cpp @@ -0,0 +1,568 @@ +/** + * @file /cryptron/ecies.c + * + * @brief ECIES encryption/decryption functions. + * + * $Author: Ladar Levison $ + * $Website: http://lavabit.com $ + * + */ + +#include "ies.h" +#include +#include +#include + +#define SET_ERROR(string) \ + sprintf(error, "%s %s:%d", (string), __FILE__, __LINE__) +#define SET_OSSL_ERROR(string) \ + sprintf(error, "%s {error = %s} %s:%d", (string), ERR_error_string(ERR_get_error(), NULL), __FILE__, __LINE__) + +/* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + * Taken from openssl/crypto/ecdh/ech_kdf.c in github:openssl/openssl + * ffa08b3242e0f10f1fef3c93ef3f0b51de8c27a9 */ + +/* Key derivation function from X9.62/SECG */ +/* Way more than we will ever need */ +#define ECDH_KDF_MAX (1 << 30) +int ECDH_KDF_X9_62(unsigned char *out, size_t outlen, + const unsigned char *Z, size_t Zlen, + const unsigned char *sinfo, size_t sinfolen, + const EVP_MD *md) +{ + EVP_MD_CTX mctx; + int rv = 0; + unsigned int i; + size_t mdlen; + unsigned char ctr[4]; + if (sinfolen > ECDH_KDF_MAX || outlen > ECDH_KDF_MAX || Zlen > ECDH_KDF_MAX) + return 0; + mdlen = EVP_MD_size(md); + EVP_MD_CTX_init(&mctx); + for (i = 1;;i++) + { + unsigned char mtmp[EVP_MAX_MD_SIZE]; + EVP_DigestInit_ex(&mctx, md, NULL); + ctr[3] = i & 0xFF; + ctr[2] = (i >> 8) & 0xFF; + ctr[1] = (i >> 16) & 0xFF; + ctr[0] = (i >> 24) & 0xFF; + if (!EVP_DigestUpdate(&mctx, Z, Zlen)) + goto err; + if (!EVP_DigestUpdate(&mctx, ctr, sizeof(ctr))) + goto err; + if (!EVP_DigestUpdate(&mctx, sinfo, sinfolen)) + goto err; + if (outlen >= mdlen) + { + if (!EVP_DigestFinal(&mctx, out, NULL)) + goto err; + outlen -= mdlen; + if (outlen == 0) + break; + out += mdlen; + } + else + { + if (!EVP_DigestFinal(&mctx, mtmp, NULL)) + goto err; + memcpy(out, mtmp, outlen); + OPENSSL_cleanse(mtmp, mdlen); + break; + } + } + rv = 1; + err: + EVP_MD_CTX_cleanup(&mctx); + return rv; +} + +static size_t envelope_key_len(const ies_ctx_t *ctx) +{ + return EVP_CIPHER_key_length(ctx->cipher) + EVP_MD_size(ctx->md); +} + +static EC_KEY * ecies_key_create(const EC_KEY *user, char *error) { + + const EC_GROUP *group; + EC_KEY *key = NULL; + + if (!(key = EC_KEY_new())) { + SET_OSSL_ERROR("EC_KEY_new failed"); + return NULL; + } + + if (!(group = EC_KEY_get0_group(user))) { + SET_ERROR("The user key does not have group"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_group(key, group) != 1) { + SET_OSSL_ERROR("EC_KEY_set_group failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_generate_key(key) != 1) { + SET_OSSL_ERROR("EC_KEY_generate_key failed"); + EC_KEY_free(key); + return NULL; + } + + return key; +} + +static unsigned char *prepare_envelope_key(const ies_ctx_t *ctx, cryptogram_t *cryptogram, char *error) +{ + + const size_t key_buf_len = envelope_key_len(ctx); + const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; + unsigned char *envelope_key = NULL, *ktmp = NULL; + EC_KEY *ephemeral = NULL; + size_t written_length; + + /* High-level ECDH via EVP does not allow use of arbitrary KDF function. + * We should use low-level API for KDF2 + * c.f. openssl/crypto/ec/ec_pmeth.c */ + if ((envelope_key = (unsigned char *) OPENSSL_malloc(key_buf_len)) == NULL) { + SET_ERROR("Failed to allocate memory for envelope_key"); + goto err; + } + + if (!(ephemeral = ecies_key_create(ctx->user_key, error))) { + goto err; + } + + /* key agreement and KDF + * reference: openssl/crypto/ec/ec_pmeth.c */ + ktmp = (unsigned char *) OPENSSL_malloc(ecdh_key_len); + if (ktmp == NULL) { + SET_ERROR("No memory for ECDH temporary key"); + goto err; + } + + if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ctx->user_key), ephemeral, NULL) + != (int)ecdh_key_len) { + SET_OSSL_ERROR("An error occurred while ECDH_compute_key"); + goto err; + } + + /* equals to ISO 18033-2 KDF2 */ + if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) { + SET_OSSL_ERROR("Failed to stretch with KDF2"); + goto err; + } + + /* Store the public key portion of the ephemeral key. */ + written_length = EC_POINT_point2oct( + EC_KEY_get0_group(ephemeral), + EC_KEY_get0_public_key(ephemeral), + POINT_CONVERSION_COMPRESSED, + cryptogram_key_data(cryptogram), + ctx->stored_key_length, + NULL); + if (written_length == 0) { + SET_OSSL_ERROR("Error while recording the public portion of the envelope key"); + goto err; + } + if (written_length != ctx->stored_key_length) { + SET_ERROR("Written envelope key length does not match with expected"); + goto err; + } + + EC_KEY_free(ephemeral); + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + + return envelope_key; + + err: + if (ephemeral) + EC_KEY_free(ephemeral); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, key_buf_len); + OPENSSL_free(envelope_key); + } + if (ktmp) { + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + } + return NULL; +} + +static int store_cipher_body( + const ies_ctx_t *ctx, + const unsigned char *envelope_key, + const unsigned char *data, + size_t length, + cryptogram_t *cryptogram, + char *error) +{ + int out_len, len_sum = 0; + size_t expected_len = cryptogram_body_length(cryptogram); + unsigned char iv[EVP_MAX_IV_LENGTH]; + EVP_CIPHER_CTX cipher; + unsigned char *body; + + /* For now we use an empty initialization vector. */ + memset(iv, 0, EVP_MAX_IV_LENGTH); + + EVP_CIPHER_CTX_init(&cipher); + body = cryptogram_body_data(cryptogram); + + if (EVP_EncryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1 + || EVP_EncryptUpdate(&cipher, body, &out_len, data, length) != 1) { + SET_OSSL_ERROR("Error while trying to secure the data using the symmetric cipher"); + EVP_CIPHER_CTX_cleanup(&cipher); + return 0; + } + + if (expected_len < (size_t)out_len) { + SET_ERROR("The symmetric cipher overflowed"); + EVP_CIPHER_CTX_cleanup(&cipher); + return 0; + } + + body += out_len; + len_sum += out_len; + if (EVP_EncryptFinal_ex(&cipher, body, &out_len) != 1) { + SET_OSSL_ERROR("Error while finalizing the data using the symmetric cipher"); + EVP_CIPHER_CTX_cleanup(&cipher); + cryptogram_free(cryptogram); + return 0; + } + + EVP_CIPHER_CTX_cleanup(&cipher); + + if (expected_len < (size_t)len_sum) { + SET_ERROR("The symmetric cipher overflowed"); + return 0; + } + + return 1; +} + +static int store_mac_tag(const ies_ctx_t *ctx, const unsigned char *envelope_key, cryptogram_t *cryptogram, char *error) { + const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher); + const size_t key_length = EVP_MD_size(ctx->md); + const size_t mac_length = cryptogram_mac_length(cryptogram); + unsigned int out_len; + HMAC_CTX hmac; + + HMAC_CTX_init(&hmac); + + /* Generate hash tag using encrypted data */ + if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1 + || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1 + || HMAC_Final(&hmac, cryptogram_mac_data(cryptogram), &out_len) != 1) { + SET_OSSL_ERROR("Unable to generate tag"); + HMAC_CTX_cleanup(&hmac); + return 0; + } + + HMAC_CTX_cleanup(&hmac); + + if (out_len != mac_length) { + SET_ERROR("MAC length expectation does not meet"); + return 0; + } + + return 1; +} + +cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error) { + + const size_t block_length = EVP_CIPHER_block_size(ctx->cipher); + const size_t mac_length = EVP_MD_size(ctx->md); + cryptogram_t *cryptogram = NULL; + unsigned char *envelope_key = NULL; + + if (!ctx || !data || !length) { + SET_ERROR("Invalid arguments"); + return NULL; + } + + if (block_length == 0 || block_length > EVP_MAX_BLOCK_LENGTH) { + SET_ERROR("Derived block size is incorrect"); + return NULL; + } + + cryptogram = cryptogram_alloc(ctx->stored_key_length, + mac_length, + length + (length % block_length ? (block_length - (length % block_length)) : 0)); + if (!cryptogram) { + SET_ERROR("Unable to allocate a cryptogram_t buffer to hold the encrypted result."); + goto err; + } + + if ((envelope_key = prepare_envelope_key(ctx, cryptogram, error)) == NULL) { + goto err; + } + + if (!store_cipher_body(ctx, envelope_key, data, length, cryptogram, error)) { + goto err; + } + + if (!store_mac_tag(ctx, envelope_key, cryptogram, error)) { + goto err; + } + + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + + return cryptogram; + + err: + if (cryptogram) + cryptogram_free(cryptogram); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + } + return NULL; +} + +static EC_KEY *ecies_key_create_public_octets(EC_KEY *user, unsigned char *octets, size_t length, char *error) { + + EC_KEY *key = NULL; + EC_POINT *point = NULL; + const EC_GROUP *group = NULL; + + if (!(key = EC_KEY_new())) { + SET_OSSL_ERROR("Cannot create instance for ephemeral key"); + return NULL; + } + + if (!(group = EC_KEY_get0_group(user))) { + SET_ERROR("Cannot get group from user key"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_group(key, group) != 1) { + SET_OSSL_ERROR("EC_KEY_set_group failed"); + EC_KEY_free(key); + return NULL; + } + + if (!(point = EC_POINT_new(group))) { + SET_OSSL_ERROR("EC_POINT_new failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_POINT_oct2point(group, point, octets, length, NULL) != 1) { + SET_OSSL_ERROR("EC_POINT_oct2point failed"); + EC_KEY_free(key); + return NULL; + } + + if (EC_KEY_set_public_key(key, point) != 1) { + SET_OSSL_ERROR("EC_KEY_set_public_key failed"); + EC_POINT_free(point); + EC_KEY_free(key); + return NULL; + } + + EC_POINT_free(point); + + if (EC_KEY_check_key(key) != 1) { + SET_OSSL_ERROR("EC_KEY_check_key failed"); + EC_KEY_free(key); + return NULL; + } + + return key; +} + +unsigned char *restore_envelope_key(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, char *error) +{ + + const size_t key_buf_len = envelope_key_len(ctx); + const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8; + EC_KEY *ephemeral = NULL, *user_copy = NULL; + unsigned char *envelope_key = NULL, *ktmp = NULL; + + if ((envelope_key = (unsigned char *) OPENSSL_malloc(key_buf_len)) == NULL) { + SET_ERROR("Failed to allocate memory for envelope_key"); + goto err; + } + + if (!(user_copy = EC_KEY_new())) { + SET_OSSL_ERROR("Failed to create instance for user key copy"); + goto err; + } + + if (!(EC_KEY_copy(user_copy, ctx->user_key))) { + SET_OSSL_ERROR("Failed to copy user key"); + goto err; + } + + if (!(ephemeral = ecies_key_create_public_octets(user_copy, cryptogram_key_data(cryptogram), cryptogram_key_length(cryptogram), error))) { + goto err; + } + + /* key agreement and KDF + * reference: openssl/crypto/ec/ec_pmeth.c */ + ktmp = (unsigned char *) OPENSSL_malloc(ecdh_key_len); + if (ktmp == NULL) { + SET_ERROR("No memory for ECDH temporary key"); + goto err; + } + + if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ephemeral), user_copy, NULL) + != (int)ecdh_key_len) { + SET_OSSL_ERROR("An error occurred while ECDH_compute_key"); + goto err; + } + + /* equals to ISO 18033-2 KDF2 */ + if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) { + SET_OSSL_ERROR("Failed to stretch with KDF2"); + goto err; + } + + EC_KEY_free(user_copy); + EC_KEY_free(ephemeral); + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + + return envelope_key; + + err: + if (ephemeral) + EC_KEY_free(ephemeral); + if (user_copy) + EC_KEY_free(user_copy); + if (envelope_key) { + OPENSSL_cleanse(envelope_key, key_buf_len); + OPENSSL_free(envelope_key); + } + if (ktmp) { + OPENSSL_cleanse(ktmp, ecdh_key_len); + OPENSSL_free(ktmp); + } + return NULL; +} + +static int verify_mac(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char * envelope_key, char *error) +{ + const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher); + const size_t key_length = EVP_MD_size(ctx->md); + const size_t mac_length = cryptogram_mac_length(cryptogram); + unsigned int out_len; + HMAC_CTX hmac; + unsigned char md[EVP_MAX_MD_SIZE]; + + HMAC_CTX_init(&hmac); + + /* Generate hash tag using encrypted data */ + if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1 + || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1 + || HMAC_Final(&hmac, md, &out_len) != 1) { + SET_OSSL_ERROR("Unable to generate tag"); + HMAC_CTX_cleanup(&hmac); + return 0; + } + + HMAC_CTX_cleanup(&hmac); + + if (out_len != mac_length) { + SET_ERROR("MAC length expectation does not meet"); + return 0; + } + + if (memcmp(md, cryptogram_mac_data(cryptogram), mac_length) != 0) { + SET_ERROR("MAC tag verification failed"); + return 0; + } + + return 1; +} + +unsigned char *decrypt_body(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char *envelope_key, size_t *length, char *error) +{ + int out_len; + size_t output_sum; + const size_t body_length = cryptogram_body_length(cryptogram); + unsigned char iv[EVP_MAX_IV_LENGTH], *block, *output; + EVP_CIPHER_CTX cipher; + + if (!(output = (unsigned char*)malloc(body_length + 1))) { + SET_ERROR("Failed to allocate memory for clear text"); + return NULL; + } + + /* For now we use an empty initialization vector */ + memset(iv, 0, EVP_MAX_IV_LENGTH); + memset(output, 0, body_length + 1); + + EVP_CIPHER_CTX_init(&cipher); + + block = output; + if (EVP_DecryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1 + || EVP_DecryptUpdate(&cipher, block, &out_len, cryptogram_body_data(cryptogram), body_length) != 1) { + SET_OSSL_ERROR("Unable to decrypt"); + EVP_CIPHER_CTX_cleanup(&cipher); + free(output); + return NULL; + } + output_sum = out_len; + + block += output_sum; + if (EVP_DecryptFinal_ex(&cipher, block, &out_len) != 1) { + printf("Unable to decrypt the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_cleanup(&cipher); + free(output); + return NULL; + } + output_sum += out_len; + + EVP_CIPHER_CTX_cleanup(&cipher); + + *length = output_sum; + + return output; +} + +unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error) +{ + unsigned char *envelope_key = NULL, *output = NULL; + + if (!ctx || !cryptogram || !length || !error) { + SET_ERROR("Invalid argument"); + goto err; + } + + envelope_key = restore_envelope_key(ctx, cryptogram, error); + if (envelope_key == NULL) { + goto err; + } + + if (!verify_mac(ctx, cryptogram, envelope_key, error)) { + goto err; + } + + if ((output = decrypt_body(ctx, cryptogram, envelope_key, length, error)) == NULL) { + goto err; + } + + err: + OPENSSL_cleanse(envelope_key, envelope_key_len(ctx)); + OPENSSL_free(envelope_key); + + return output; +} + +ies_ctx_t *create_context(EC_KEY *user_key) +{ + ies_ctx_t* ctx = (ies_ctx_t*) malloc(sizeof(ies_ctx_t)); + ctx->cipher = EVP_aes_128_cbc(); + ctx->md = EVP_sha1(); + ctx->kdf_md = EVP_sha1(); + ctx->stored_key_length = 33; + ctx->user_key = user_key; + + return ctx; +} diff --git a/src/ies.h b/src/ies.h new file mode 100644 index 0000000..bf69a82 --- /dev/null +++ b/src/ies.h @@ -0,0 +1,44 @@ +/** + * reference: ecies.h + */ + +#ifndef _IES_H_ +#define _IES_H_ + +#include +#include +#include + +typedef struct { + const EVP_CIPHER *cipher; + const EVP_MD *md; /* for mac tag */ + const EVP_MD *kdf_md; /* for KDF */ + size_t stored_key_length; + const EC_KEY *user_key; +} ies_ctx_t; + +typedef struct { + struct { + size_t key; + size_t mac; + size_t body; + } length; +} cryptogram_head_t; + +typedef unsigned char * cryptogram_t; + +void cryptogram_free(cryptogram_t *cryptogram); +unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram); +unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram); +unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram); +size_t cryptogram_key_length(const cryptogram_t *cryptogram); +size_t cryptogram_mac_length(const cryptogram_t *cryptogram); +size_t cryptogram_body_length(const cryptogram_t *cryptogram); +size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram); +size_t cryptogram_total_length(const cryptogram_t *cryptogram); +cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body); +cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error); +unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error); +ies_ctx_t *create_context(EC_KEY *user_key); + +#endif /* _IES_H_ */ diff --git a/src/key.cpp b/src/key.cpp index afe63cb..eb4f5c4 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include "key.h" #include "base58.h" +#include "ies.h" // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) @@ -1095,16 +1097,65 @@ bool CMalleableKeyView::CheckKeyVariant(const CPubKey &R, const CPubKey &vchPubK //// Asymmetric encryption -bool CPubKey::EncryptData(const std::vector& data, std::vector& encrypted) +void CPubKey::EncryptData(const std::vector& data, std::vector& encrypted) { - // TODO + CKey key; + key.SetPubKey(*this); - return true; + key.EncryptData(data, encrypted); } -bool CKey::DecryptData(const std::vector& encrypted, std::vector& data) +void CKey::EncryptData(const std::vector& data, std::vector& 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& encrypted, std::vector& data) +{ + ies_ctx_t *ctx; + char error[1024] = "Unknown error"; + cryptogram_t *cryptogram; + size_t length; + unsigned char *decrypted; + + ctx = create_context(pkey); + if (!EC_KEY_get0_private_key(ctx->user_key)) + throw key_error("Given EC key is not private key"); + + size_t key_length = ctx->stored_key_length; + size_t mac_length = EVP_MD_size(ctx->md); + cryptogram = cryptogram_alloc(key_length, mac_length, encrypted.size() - key_length - mac_length); + + memcpy(cryptogram_key_data(cryptogram), &encrypted[0], encrypted.size()); + + decrypted = ecies_decrypt(ctx, cryptogram, &length, error); + cryptogram_free(cryptogram); + free(ctx); + + if (decrypted == NULL) { + throw key_error(std::string("Error in decryption: %s") + error); + } + + data.resize(length); + memcpy(&data[0], decrypted, length); + free(decrypted); } diff --git a/src/key.h b/src/key.h index a3b23fe..f8404db 100644 --- a/src/key.h +++ b/src/key.h @@ -13,6 +13,7 @@ #include "uint256.h" #include "hash.h" #include "bignum.h" +#include "ies.h" #include // for EC_KEY definition @@ -99,7 +100,7 @@ public: } // Encrypt data - bool EncryptData(const std::vector& data, std::vector& encrypted); + void EncryptData(const std::vector& data, std::vector& encrypted); }; @@ -168,8 +169,11 @@ public: // Reserialize to DER static bool ReserealizeSignature(std::vector& vchSig); + // Encrypt data + void EncryptData(const std::vector& data, std::vector& encrypted); + // Decrypt data - bool DecryptData(const std::vector& encrypted, std::vector& data); + void DecryptData(const std::vector& encrypted, std::vector& data); }; class CPoint diff --git a/src/makefile.bsd b/src/makefile.bsd index 786d5ef..a22ce33 100644 --- a/src/makefile.bsd +++ b/src/makefile.bsd @@ -131,7 +131,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 080af03..e255112 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -98,7 +98,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind.exe diff --git a/src/makefile.mingw b/src/makefile.mingw index feacdef..e0bd3e0 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -88,7 +88,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind.exe diff --git a/src/makefile.osx b/src/makefile.osx index 388c5be..96cf542 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -95,7 +95,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o ifneq (${USE_IPV6}, -) DEFS += -DUSE_IPV6=$(USE_IPV6) diff --git a/src/makefile.unix b/src/makefile.unix index 6ec04ce..a37bf0e 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -132,7 +132,9 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/kernel_worker.o + obj/kernel_worker.o \ + obj/ecies.o \ + obj/cryptogram.o all: novacoind diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 22beb60..c1be536 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -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 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 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 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; } -- 1.7.1